From 05c54827152157983661a391c73d04b2615638c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 23 Oct 2024 23:23:40 +0800 Subject: [PATCH 001/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E9=9B=86=E6=88=90=20tdengine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 7 + .../yudao-spring-boot-starter-mybatis/pom.xml | 4 + .../yudao/module/iot/domain/BaseEntity.java | 25 ++ .../iot/domain/DeviceDataExportExcelDto.java | 35 ++ .../yudao/module/iot/domain/DeviceDataVo.java | 19 + .../yudao/module/iot/domain/Fields.java | 72 ++++ .../yudao/module/iot/domain/FieldsVo.java | 68 ++++ .../module/iot/domain/IotSequential.java | 71 ++++ .../module/iot/domain/MessageCountVo.java | 25 ++ .../iot/domain/ProductSuperTableModel.java | 39 +++ .../yudao/module/iot/domain/SelectDto.java | 37 ++ .../module/iot/domain/SuperTableDto.java | 38 ++ .../yudao/module/iot/domain/TableDto.java | 33 ++ .../module/iot/domain/TagsSelectDao.java | 33 ++ .../yudao/module/iot/domain/Weather.java | 73 ++++ .../iot/domain/visual/SelectVisualDto.java | 58 ++++ .../product/IotProductFunctionTypeEnum.java | 9 + yudao-module-iot/yudao-module-iot-biz/pom.xml | 5 + .../admin/product/IotProductController.java | 5 +- .../thingModel/ThingModelRespVO.java | 51 +++ .../dal/dataobject/tdengine/FieldParser.java | 80 +++++ .../dal/dataobject/tdengine/TableData.java | 36 ++ .../dal/dataobject/tdengine/TableManager.java | 155 +++++++++ .../iot/dal/dataobject/tdengine/TdField.java | 29 ++ .../dal/dataobject/tdengine/TdResponse.java | 26 ++ .../dal/dataobject/tdengine/TdRestApi.java | 55 +++ .../iot/dal/dataobject/tdengine/TimeData.java | 25 ++ .../iot/dal/tdengine/TdEngineMapper.java | 74 ++++ .../iot/framework/aspect/TaosAspect.java | 38 ++ .../product/IotProductServiceImpl.java | 15 + .../tdengine/IotDbStructureDataService.java | 29 ++ .../IotDbStructureDataServiceImpl.java | 168 +++++++++ .../iot/service/tdengine/TdEngineService.java | 141 ++++++++ .../service/tdengine/TdEngineServiceImpl.java | 93 +++++ .../IotThinkModelFunctionService.java | 6 + .../IotThinkModelFunctionServiceImpl.java | 18 + .../mapper/tdengine/TdEngineMapper.xml | 324 ++++++++++++++++++ 37 files changed, 2016 insertions(+), 3 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 924e52eedd..486fe124b3 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -33,6 +33,7 @@ 8.1.3.62 8.6.0 5.0.2 + 3.3.3 2.3.0 @@ -253,6 +254,12 @@ ${kingbase.jdbc.version} + + com.taosdata.jdbc + taos-jdbcdriver + ${taos.version} + + cn.iocoder.boot diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 1f04e4a635..e907a2de4d 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -63,6 +63,10 @@ opengauss-jdbc true + + com.taosdata.jdbc + taos-jdbcdriver + com.alibaba diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java new file mode 100644 index 0000000000..74a4c8494b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +/** + * @ClassDescription: tdEngine的基础实体类 + * @ClassName: BaseEntity + * @Author: fxlinks + * @Date: 2021-12-30 14:39:25 + * @Version 1.0 + */ +@Data +public class BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 数据库名称 + */ + private String dataBaseName; + + /** + * 超级表名称 + */ + private String superTableName; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java new file mode 100644 index 0000000000..cedb4d1190 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +/** + * 设备数据导出 excel DTO + */ +@Data +public class DeviceDataExportExcelDto { + + /** + * 设备标识 + */ + private String deviceKey; + + /** + * 导出形式 1 单个参数导出 2 全部参数导出 + */ + private String exportType; + + /** + * 导出开始时间 + */ + private String exportBeginTime; + + /** + * 导出结束时间 + */ + private String exportEndTime; + + /** + * 导出参数,空则导出全部 + */ + private String exportParameter; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java new file mode 100644 index 0000000000..b9f6fc4127 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +/** + * @ClassDescription: 查询可视化所需入参对象 + * @ClassName: SelectDto + * @Author: andyz + * @Date: 2022-07-29 14:12:26 + * @Version 1.0 + */ +@Data +public class DeviceDataVo { + + + private String deviceId; + + private Long lastTime; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java new file mode 100644 index 0000000000..bbd7bb74e8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +/** + * @ClassDescription: 建表的字段实体类 + * @ClassName: Fields + * @Author: fxlinks + * @Date: 2021-12-28 09:09:04 + * @Version 1.0 + */ +@Data +public class Fields { + private static final long serialVersionUID = 1L; + + /** + * 字段名称 + */ + private String fieldName; + + /** + * 字段值 + */ + private Object fieldValue; + + /** + * 字段数据类型 + */ +// private DataTypeEnum dataType; + + /** + * 字段字节大小 + */ + private Integer size; + + public Fields() { + } + + public Fields(String fieldName, String dataType, Integer size) { +// this.fieldName = fieldName; +// //根据规则匹配字段数据类型 +// switch (dataType.toLowerCase()) { +// case ("json"): +// this.dataType = DataTypeEnum.JSON; +// this.size = size; +// break; +// case ("string"): +// this.dataType = DataTypeEnum.NCHAR; +// this.size = size; +// break; +// case ("binary"): +// this.dataType = DataTypeEnum.BINARY; +// this.size = size; +// break; +// case ("int"): +// this.dataType = DataTypeEnum.INT; +// break; +// case ("bool"): +// this.dataType = DataTypeEnum.BOOL; +// break; +// case ("decimal"): +// this.dataType = DataTypeEnum.DOUBLE; +// break; +// case ("timestamp"): +// if ("eventTime".equals(fieldName)) { +// this.fieldName = "eventTime"; +// } +// this.dataType = DataTypeEnum.TIMESTAMP; +// break; +// } + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java new file mode 100644 index 0000000000..554d52149d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * @ClassDescription: 建表的字段实体类的vo类 + * @ClassName: FieldsVo + * @Author: fxlinks + * @Date: 2021-12-28 11:30:06 + * @Version 1.0 + */ +@Data +public class FieldsVo { + private static final long serialVersionUID = 1L; + + /** + * 字段名称 + */ + private String fieldName; + + /** + * 字段数据类型 + */ + private String dataType; + + /** + * 字段字节大小 + */ + private Integer size; + + /** + * @param fields 字段实体类 + * @return FieldsVo 字段实体vo类 + * @MethodDescription 字段实体类转为vo类 + * @author fx + * @Date 2021/12/28 13:48 + */ + public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException { +// if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) { +// throw new SQLException("invalid operation: fieldName or dataType can not be null"); +// } +// FieldsVo fieldsVo = new FieldsVo(); +// fieldsVo.setFieldName(fields.getFieldName()); +// fieldsVo.setDataType(fields.getDataType().getDataType()); +// fieldsVo.setSize(fields.getSize()); +// return fieldsVo; + return null; + } + + /** + * @param fieldsList 字段实体类集合 + * @return List 字段实体vo类集合 + * @MethodDescription 字段实体类集合转为vo类集合 + * @author fx + * @Date 2021/12/28 14:00 + */ + public static List fieldsTranscoding(List fieldsList) throws SQLException { + List fieldsVoList = new ArrayList<>(); + for (Fields fields : fieldsList) { + fieldsVoList.add(fieldsTranscoding(fields)); + } + return fieldsVoList; + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java new file mode 100644 index 0000000000..349d6d1371 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.iot.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.sql.Timestamp; + +public class IotSequential extends BaseEntity { + private static final long serialVersionUID = 1L; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") + private Timestamp statetime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") + private Timestamp endtime; + + private String deviceid; + + private String eventtime; + + private String serviceid; + + private String devices; + + public String getDeviceid() { + return deviceid; + } + + public void setDeviceid(String deviceid) { + this.deviceid = deviceid; + } + + public String getEventtime() { + return eventtime; + } + + public void setEventtime(String eventtime) { + this.eventtime = eventtime; + } + + public String getServiceid() { + return serviceid; + } + + public void setServiceid(String serviceid) { + this.serviceid = serviceid; + } + + public String getDevices() { + return devices; + } + + public void setDevices(String devices) { + this.devices = devices; + } + + public Timestamp getStatetime() { + return statetime; + } + + public void setStatetime(Timestamp statetime) { + this.statetime = statetime; + } + + public Timestamp getEndtime() { + return endtime; + } + + public void setEndtime(Timestamp endtime) { + this.endtime = endtime; + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java new file mode 100644 index 0000000000..e9e93cac01 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 统计的时间数据 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MessageCountVo { + + /** + * 时间 + */ + private String time; + + /** + * 数据值 + */ + private Object data; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java new file mode 100644 index 0000000000..49887415c7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Optional; + +/** + * @Description: 产品超级表模型 + * @Author: fx + * @Website: http://www.sxfxck.com + * @CreateDate: 2022/1/1$ 19:37$ + * @UpdateUser: fx + * @UpdateDate: 2022/1/1$ 19:37$ + * @UpdateRemark: 修改内容 + * @Version: V1.0 + */ +@Data +public class ProductSuperTableModel { + private static final long serialVersionUID = 1L; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") + private Timestamp ts; + + private String superTableName; + + /** + * columnsName,columnsProperty + */ + private HashMap columns; + + /** + * tagsName,tagsProperty + */ + private HashMap tags; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java new file mode 100644 index 0000000000..381ef3d6f1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +import java.util.Set; + +/** + * @ClassDescription: 查询所需入参对象 + * @ClassName: SelectDto + * @Author: fxlinks + * @Date: 2022-01-07 14:12:26 + * @Version 1.0 + */ +@Data +public class SelectDto { + + // @NotBlank(message = "invalid operation: dataBaseName can not be empty") + private String dataBaseName; + +// @NotBlank(message = "invalid operation: tableName can not be empty") + private String tableName; + + // @NotBlank(message = "invalid operation: fieldName can not be empty") + private String fieldName; + + // @NotNull(message = "invalid operation: startTime can not be null") + private Long startTime; + + // @NotNull(message = "invalid operation: endTime can not be null") + private Long endTime; + + private String type; + + private Set orgIds; + + private String deviceId; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java new file mode 100644 index 0000000000..ee80d42cfd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +import java.util.List; + +/** + * @ClassDescription: 创建超级表需要的入参的实体类 + * @ClassName: SuperTableDto + * @Author: fxlinks + * @Date: 2021-12-28 15:03:45 + * @Version 1.0 + */ +@Data +public class SuperTableDto extends BaseEntity { + + /** + * 超级表的表结构(业务相关) + * 第一个字段的数据类型必须为timestamp + * 字符相关数据类型必须指定大小 + * 字段名称和字段数据类型不能为空 + */ +// @NotEmpty(message = "invalid operation: schemaFields can not be empty") + private List schemaFields; + + /** + * 超级表的标签字段,可以作为子表在超级表里的标识 + * 字符相关数据类型必须指定大小 + * 字段名称和字段数据类型不能为空 + */ +// @NotEmpty(message = "invalid operation: tagsFields can not be empty") + private List tagsFields; + + /** + * 字段信息对象,超级表添加列时使用该属性 + */ + private Fields fields; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java new file mode 100644 index 0000000000..f27beb1a69 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + +import java.util.List; + +/** + * @ClassDescription: 创建超级表的子表需要的入参的实体类 + * @ClassName: TableDto + * @Author: fxlinks + * @Date: 2021-12-30 14:42:47 + * @Version 1.0 + */ +@Data +public class TableDto extends BaseEntity { + + /** + * 超级表普通列字段的值 + * 值需要与创建超级表时普通列字段的数据类型对应上 + */ + private List schemaFieldValues; + + /** + * 超级表标签字段的值 + * 值需要与创建超级表时标签字段的数据类型对应上 + */ + private List tagsFieldValues; + + /** + * 表名称 + */ + private String tableName; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java new file mode 100644 index 0000000000..5cb9c5ed92 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.domain; + +import lombok.Data; + + +/** + * @program: fxlinks + * @description: 标签查询模型 + * @packagename: com.fx.tdengine.api.domain.rule + * @author: fx + * @e-mainl: 13733918655@163.com + * @date: 2022-07-27 18:40 + **/ +@Data +public class TagsSelectDao { + +// @NotBlank(message = "invalid operation: dataBaseName can not be empty") + private String dataBaseName; + +// @NotBlank(message = "invalid operation: stableName can not be empty") + private String stableName; + +// @NotBlank(message = "invalid operation: tagsName can not be empty") + private String tagsName; + + // @NotNull(message = "invalid operation: startTime can not be null") + private Long startTime; + + // @NotNull(message = "invalid operation: endTime can not be null") + private Long endTime; + + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java new file mode 100644 index 0000000000..a4a1e983b8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.iot.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.sql.Timestamp; + +public class Weather { + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") + private Timestamp ts; + private Float temperature; + private Float humidity; + private String location; + private String note; + private int groupId; + + public Weather() { + } + + public Weather(Timestamp ts, float temperature, float humidity) { + this.ts = ts; + this.temperature = temperature; + this.humidity = humidity; + } + + public Timestamp getTs() { + return ts; + } + + public void setTs(Timestamp ts) { + this.ts = ts; + } + + public Float getTemperature() { + return temperature; + } + + public void setTemperature(Float temperature) { + this.temperature = temperature; + } + + public Float getHumidity() { + return humidity; + } + + public void setHumidity(Float humidity) { + this.humidity = humidity; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public int getGroupId() { + return groupId; + } + + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java new file mode 100644 index 0000000000..1444da9727 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.iot.domain.visual; + +import lombok.Data; + +import java.util.Map; + +/** + * @ClassDescription: 查询可视化所需入参对象 + * @ClassName: SelectDto + * @Author: andyz + * @Date: 2022-07-29 14:12:26 + * @Version 1.0 + */ +@Data +public class SelectVisualDto { + +// @NotBlank(message = "invalid operation: tableName can not be empty") + private String dataBaseName; + +// @NotBlank(message = "invalid operation: tableName can not be empty") + private String tableName; + +// @NotBlank(message = "invalid operation: fieldName can not be empty") //属性 + private String fieldName; + /** + * 查询类型,0历史数据,1实时数据,2聚合数据 + */ + private int type; + /** + * 查询的数据量 + */ + private int num; + /** + * 聚合函数 + */ + private String aggregate; + /** + * 统计间隔数字+s/m/h/d + * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天 + */ + private String interval; + // @NotNull(message = "invalid operation: startTime can not be null") + private Long startTime; + + // @NotNull(message = "invalid operation: endTime can not be null") + private Long endTime; + + /** + * 请求参数 + */ + private Map params; + + private String sql; + + private String deviceId; + + private Long lastTime; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java index 7a924997a5..b99d2b0938 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java @@ -30,6 +30,15 @@ public enum IotProductFunctionTypeEnum implements IntArrayValuable { */ private final String description; + public static IotProductFunctionTypeEnum valueOf(Integer type) { + for (IotProductFunctionTypeEnum value : values()) { + if (value.getType().equals(type)) { + return value; + } + } + return null; + } + @Override public int[] array() { return ARRAYS; diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index e3f93086ae..cb2fd818f9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -42,6 +42,11 @@ yudao-spring-boot-starter-mybatis + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + cn.iocoder.boot diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java index 5b0ecb27ae..e7f41d91dc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java @@ -83,11 +83,10 @@ public class IotProductController { return success(BeanUtils.toBean(pageResult, IotProductRespVO.class)); } - // TODO @haohao:改成 simple-list 哈 - @GetMapping("/list-all-simple") + @GetMapping("/simple-list") @Operation(summary = "获得所有产品列表") @PreAuthorize("@ss.hasPermission('iot:product:query')") - public CommonResult> listAllSimpleProducts() { + public CommonResult> getSimpleProductList() { List list = productService.getProductList(); return success(BeanUtils.toBean(list, IotProductSimpleRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java new file mode 100644 index 0000000000..6fc48ef4f7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel; + +import lombok.*; + +import java.util.List; +import java.util.Map; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@ToString +public class ThingModelRespVO { + + /** + * 产品编号 + */ + private Long id; + + /** + * 产品标识 + */ + private String productKey; + + /** + * 物模型 + */ + private Model model; + + /** + * 物模型 + */ + @Data + public static class Model { + + /** + * 属性列表 + */ + private List properties; + + /** + * 服务列表 + */ + private List services; + + /** + * 事件列表 + */ + private List events; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java new file mode 100644 index 0000000000..0a91e7cf19 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + + +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType; + +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class FieldParser { + + /** + * 物模型到td数据类型映射 + */ + private static final HashMap TYPE_MAPPING = new HashMap<>() {{ + put("INT", "INT"); + put("FLOAT", "FLOAT"); + put("DOUBLE", "DOUBLE"); + put("BOOL", "BOOL"); + put("ENUM", "NCHAR"); + put("TEXT", "NCHAR"); + put("DATE", "NCHAR"); + }}; + + + /** + * 将物模型字段转换为td字段 + * + * @param property 物模型属性 + * @return TdField对象 + */ + public static TdField parse(ThingModelProperty property) { + String fieldName = property.getIdentifier().toLowerCase(); + ThingModelDataType type = property.getDataType(); + + // 将物模型字段类型映射为td字段类型 + String fType = TYPE_MAPPING.get(type.getType().toUpperCase()); + + int len = -1; + // 如果字段类型为NCHAR,默认长度为64 + if ("NCHAR".equals(fType)) { + len = 64; + } + + return new TdField(fieldName, fType, len); + } + + /** + * 获取物模型中的字段列表 + */ + public static List parse(ThingModelRespVO thingModel) { + return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList()); + } + + /** + * 将从库中查出来的字段信息转换为td字段对象 + */ + public static List parse(List rows) { + return (List) rows.stream().map((r) -> { + List row = (List) r; + String type = row.get(1).toString().toUpperCase(); + return new TdField( + row.get(0).toString(), + type, + type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1); + }).collect(Collectors.toList()); + } + + /** + * 获取字段字义 + */ + public static String getFieldDefine(TdField field) { + return "`" + field.getName() + "`" + " " + (field.getLength() > 0 ? + String.format("%s(%d)", field.getType(), field.getLength()) + : field.getType()); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java new file mode 100644 index 0000000000..1d42933d4c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.Data; + +import java.util.List; + +@Data +public class TableData { + + /** + * 超级表普通列字段的名称 + */ + private List schemaFieldList; + + /** + * 超级表标签字段的值 + * 值需要与创建超级表时标签字段的数据类型对应上 + */ + private List tagsValueList; + + /** + * 超级表普通列字段的值 + * 值需要与创建超级表时普通列字段的数据类型对应上 + */ + private List schemaValueList; + + /** + * 表名称 + */ + private String tableName; + + /** + * 超级表名称 + */ + private String superTableName; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java new file mode 100644 index 0000000000..1bf4ecf646 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java @@ -0,0 +1,155 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import java.util.List; + +public class TableManager { + + /** + * 创建超级表模板(含存在判断) + */ + private static final String CREATE_STABLE_INE_TPL = "CREATE STABLE IF NOT EXISTS %s (%s) TAGS (%s);"; + + /** + * 删除超级表 + */ + private static final String DROP_STABLE_TPL = "DROP STABLE IF EXISTS %s;"; + + /** + * 获取表的结构信息 + */ + private static final String DESC_TB_TPL = "DESCRIBE %s;"; + + /** + * 超级表增加列 + */ + private static final String ALTER_STABLE_ADD_COL_TPL = "ALTER STABLE %s ADD COLUMN %s;"; + + /** + * 超级表修改列 + */ + private static final String ALTER_STABLE_MODIFY_COL_TPL = "ALTER STABLE %s MODIFY COLUMN %s;"; + + /** + * 超级表删除列 + */ + private static final String ALTER_STABLE_DROP_COL_TPL = "ALTER STABLE %s DROP COLUMN %s;"; + + /** + * 创建普通表模板(含存在判断) + */ + private static final String CREATE_CTABLE_INE_TPL = "CREATE TABLE IF NOT EXISTS %s (%s)"; + + /** + * 获取创建表sql + */ + public static String getCreateSTableSql(String tbName, List fields, TdField... tags) { + if (fields.isEmpty()) { + return null; + } + + // 生成字段片段 + StringBuilder sbField = new StringBuilder("time TIMESTAMP,"); + + for (TdField field : fields) { + sbField.append(FieldParser.getFieldDefine(field)); + sbField.append(","); + } + sbField.deleteCharAt(sbField.length() - 1); + + String fieldFrag = sbField.toString(); + + // 生成tag + StringBuilder sbTag = new StringBuilder(); + for (TdField tag : tags) { + sbTag.append(FieldParser.getFieldDefine(tag)) + .append(","); + } + sbTag.deleteCharAt(sbTag.length() - 1); + + return String.format(CREATE_STABLE_INE_TPL, tbName, fieldFrag, sbTag); + + } + + /** + * 获取创建普通表sql + */ + public static String getCreateCTableSql(String tbName, List fields) { + if (fields.size() == 0) { + return null; + } + + //生成字段片段 + StringBuilder sbField = new StringBuilder("time timestamp,"); + + for (TdField field : fields) { + sbField.append(FieldParser.getFieldDefine(field)); + sbField.append(","); + } + sbField.deleteCharAt(sbField.length() - 1); + + String fieldFrag = sbField.toString(); + + return String.format(CREATE_CTABLE_INE_TPL, tbName, fieldFrag); + + } + + + /** + * 取正确的表名 + * + * @param name 表象 + */ + public static String rightTbName(String name) { + return name.toLowerCase().replace("-" , "_"); + } + + /** + * 获取表详情的sql + */ + public static String getDescTableSql(String tbName) { + return String.format(DESC_TB_TPL, tbName); + } + + /** + * 获取添加字段sql + */ + public static String getAddSTableColumnSql(String tbName, List fields) { + StringBuilder sbAdd = new StringBuilder(); + for (TdField field : fields) { + sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL, + tbName, + FieldParser.getFieldDefine(field) + )); + } + return sbAdd.toString(); + } + + /** + * 获取修改字段sql + */ + public static String getModifySTableColumnSql(String tbName, List fields) { + StringBuilder sbModify = new StringBuilder(); + for (TdField field : fields) { + sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL, + tbName, + FieldParser.getFieldDefine(field) + )); + } + return sbModify.toString(); + } + + /** + * 获取删除字段sql + */ + public static String getDropSTableColumnSql(String tbName, List fields) { + StringBuilder sbDrop = new StringBuilder(); + for (TdField field : fields) { + sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL, + tbName, + field.getName() + )); + } + return sbDrop.toString(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java new file mode 100644 index 0000000000..f040a017ad --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * TD 引擎的字段 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TdField { + + /** + * 字段名称 + */ + private String name; + + /** + * 字段类型 + */ + private String type; + + /** + * 字段长度 + */ + private int length; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java new file mode 100644 index 0000000000..380807a1b8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TdResponse { + + public static final int CODE_SUCCESS = 0; + public static final int CODE_TB_NOT_EXIST = 866; + + private String status; + + private int code; + + private String desc; + + //[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] + private List data; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java new file mode 100644 index 0000000000..4506383f61 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class TdRestApi { + + @Value("${spring.datasource.dynamic.datasource.master.url}") + private String url; + + @Value("${spring.datasource.dynamic.datasource.master.username}") + private String username; + + @Value("${spring.datasource.dynamic.datasource.master.password}") + private String password; + + private String getRestApiUrl() { + //jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx + String restUrl = url.replace("jdbc:TAOS-RS://" , "") + .replaceAll("\\?.*" , ""); + // /rest/sql/iotkit + int idx = restUrl.lastIndexOf("/"); + //127.0.0.1:6041/rest/sql/iotkit + return String.format("%s/rest/sql/%s" , restUrl.substring(0, idx), restUrl.substring(idx + 1)); + } + + + /** + * 新建td api请求对象 + */ + public HttpRequest newApiRequest(String sql) { + return HttpRequest + .post(getRestApiUrl()) + .body(sql) + .basicAuth(username, password); + } + + /** + * 执行sql + */ + public TdResponse execSql(String sql) { + log.info("exec td sql:{}" , sql); + HttpRequest request = newApiRequest(sql); + HttpResponse response = request.execute(); + return JSONUtil.toBean(response.body(), TdResponse.class); + } + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java new file mode 100644 index 0000000000..f7e251ea8a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 统计的时间数据 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TimeData { + + /** + * 时间 + */ + private long time; + + /** + * 数据值 + */ + private Object data; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java new file mode 100644 index 0000000000..6a925f4e5a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.domain.SelectDto; +import cn.iocoder.yudao.module.iot.domain.TableDto; +import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +@Mapper +@DS("tdengine") +public interface TdEngineMapper { + + void createDatabase(@Param("dataBaseName") String dataBaseName); + + void createSuperTable(@Param("schemaFields") List schemaFields, + @Param("tagsFields") List tagsFields, + @Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName); + + void createTable(TableDto tableDto); + + void insertData(TableDto tableDto); + + List> selectByTimestamp(SelectDto selectDto); + + void addColumnForSuperTable(@Param("superTableName") String superTableName, + @Param("fieldsVo") FieldsVo fieldsVo); + + void dropColumnForSuperTable(@Param("superTableName") String superTableName, + @Param("fieldsVo") FieldsVo fieldsVo); + + void addTagForSuperTable(@Param("superTableName") String superTableName, + @Param("fieldsVo") FieldsVo fieldsVo); + + void dropTagForSuperTable(@Param("superTableName") String superTableName, + @Param("fieldsVo") FieldsVo fieldsVo); + + Map getCountByTimestamp(SelectDto selectDto); + + /** + * 检查表是否存在 + * + * @param dataBaseName 数据库名称 + * @param tableName 表名称 + */ + Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName); + + Map getLastData(SelectDto selectDto); + + List> getHistoryData(SelectVisualDto selectVisualDto); + + List> getRealtimeData(SelectVisualDto selectVisualDto); + + List> getAggregateData(SelectVisualDto selectVisualDto); + + List> getLastDataByTags(TagsSelectDao tagsSelectDao); + + /** + * 创建超级表 + * + * @param sql sql + * @return 返回值 + */ + @InterceptorIgnore(tenantLine = "true") + Integer createSuperTableDevice(String sql); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java new file mode 100644 index 0000000000..7c9fe70009 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.framework.aspect; + +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; + +import java.sql.Timestamp; +import java.util.Map; + +/** + * TaosAspect 是一个处理 Taos 数据库返回值的切面。 + */ +@Aspect +@Component +@Slf4j +public class TaosAspect { + + @Around("execution(java.util.Map cn.iocoder.yudao.module.iot.dal.tdengine.*.*(..))") + public Object handleType(ProceedingJoinPoint joinPoint) { + Map result = null; + try { + result = (Map) joinPoint.proceed(); + result.replaceAll((key, value) -> { + if (value instanceof byte[]) { + return new String((byte[]) value); + } else if (value instanceof Timestamp) { + return ((Timestamp) value).getTime(); + } + return value; + }); + } catch (Throwable e) { + log.error("TaosAspect handleType error", e); + } + return result; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 15391f70b3..d0ca092889 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -8,8 +8,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReq import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; + +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; @@ -31,6 +36,10 @@ public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; + @Resource + @Lazy + private IotThinkModelFunctionService thinkModelFunctionService; + @Override public Long createProduct(IotProductSaveReqVO createReqVO) { // 1. 生成 ProductKey @@ -106,11 +115,17 @@ public class IotProductServiceImpl implements IotProductService { } @Override + @DSTransactional(rollbackFor = Exception.class) public void updateProductStatus(Long id, Integer status) { // 1. 校验存在 validateProductExists(id); // 2. 更新 IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); + // 3. 产品是发布状态 + if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getType())) { + // 3.1 创建超级表数据模型 + thinkModelFunctionService.createSuperTableDataModel(id); + } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java new file mode 100644 index 0000000000..42439e25a2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + + +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; + +import java.util.List; + +/** + * 数据结构接口 + */ +public interface IotDbStructureDataService { + + /** + * 创建物模型定义 + */ + void createSuperTable(ThingModelRespVO thingModel, Integer deviceType); + + /** + * 更新物模型定义 + */ + void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType); + + /** + * 创建超级表数据模型 + */ + void createSuperTableDataModel(IotProductDO product, List functionList); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java new file mode 100644 index 0000000000..f8dd6feb5d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java @@ -0,0 +1,168 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; +import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { + + @Resource + private TdEngineMapper tdEngineMapper; + + @Resource + private TdRestApi tdRestApi; + + @Override + public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { + // 获取物模型中的属性定义 + List fields = FieldParser.parse(thingModel); + String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); + + // 生成创建超级表的 SQL + String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64)); + if (sql == null) { + log.warn("生成的 SQL 为空,无法创建超级表"); + return; + } + log.info("执行 SQL: {}", sql); + + // 执行 SQL 创建超级表 + tdEngineMapper.createSuperTableDevice(sql); + } + + @Override + public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { + try { + // 获取旧字段信息 + String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); + String sql = TableManager.getDescTableSql(tbName); + TdResponse response = tdRestApi.execSql(sql); + if (response.getCode() != TdResponse.CODE_SUCCESS) { + throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response)); + } + + List oldFields = FieldParser.parse(response.getData()); + List newFields = FieldParser.parse(thingModel); + + // 找出新增的字段 + List addFields = newFields.stream() + .filter(f -> oldFields.stream().noneMatch(old -> old.getName().equals(f.getName()))) + .collect(Collectors.toList()); + if (!addFields.isEmpty()) { + sql = TableManager.getAddSTableColumnSql(tbName, addFields); + response = tdRestApi.execSql(sql); + if (response.getCode() != TdResponse.CODE_SUCCESS) { + throw new RuntimeException("添加表字段错误: " + JSONUtil.toJsonStr(response)); + } + } + + // 找出修改的字段 + List modifyFields = newFields.stream() + .filter(f -> oldFields.stream().anyMatch(old -> + old.getName().equals(f.getName()) && + (!old.getType().equals(f.getType()) || old.getLength() != f.getLength()))) + .collect(Collectors.toList()); + if (!modifyFields.isEmpty()) { + sql = TableManager.getModifySTableColumnSql(tbName, modifyFields); + response = tdRestApi.execSql(sql); + if (response.getCode() != TdResponse.CODE_SUCCESS) { + throw new RuntimeException("修改表字段错误: " + JSONUtil.toJsonStr(response)); + } + } + + // 找出删除的字段 + List dropFields = oldFields.stream() + .filter(f -> !"time".equals(f.getName()) && !"device_id".equals(f.getName()) && + newFields.stream().noneMatch(n -> n.getName().equals(f.getName()))) + .collect(Collectors.toList()); + if (!dropFields.isEmpty()) { + sql = TableManager.getDropSTableColumnSql(tbName, dropFields); + response = tdRestApi.execSql(sql); + if (response.getCode() != TdResponse.CODE_SUCCESS) { + throw new RuntimeException("删除表字段错误: " + JSONUtil.toJsonStr(response)); + } + } + } catch (Throwable e) { + log.error("更新物模型超级表失败", e); + } + } + + @Override + public void createSuperTableDataModel(IotProductDO product, List functionList) { + // 1. 生成 ThingModelRespVO + ThingModelRespVO thingModel = new ThingModelRespVO(); + thingModel.setId(product.getId()); + thingModel.setProductKey(product.getProductKey()); + + // 1.1 设置属性、服务和事件 + ThingModelRespVO.Model model = new ThingModelRespVO.Model(); + List properties = new ArrayList<>(); + + // 1.2 遍历功能列表并分类 + for (IotThinkModelFunctionDO function : functionList) { + if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) { + ThingModelProperty property = new ThingModelProperty(); + property.setIdentifier(function.getIdentifier()); + property.setName(function.getName()); + property.setDescription(function.getDescription()); + property.setDataType(function.getProperty().getDataType()); + properties.add(property); + } + } + + // 1.3 判断属性列表是否为空 + if (properties.isEmpty()) { + log.warn("物模型属性列表为空,不创建超级表"); + return; + } + + model.setProperties(properties); + thingModel.setModel(model); + + // 2. 判断是否已经创建,如果已经创建则进行更新 + String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); + Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName); + if (iot != null && iot > 0) { + // 3. 更新 + updateSuperTable(thingModel, product.getDeviceType()); + } else { + // 4. 创建 + createSuperTable(thingModel, product.getDeviceType()); + } + } + + /** + * 根据产品key获取产品属性超级表名 + */ + static String getProductPropertySTableName(Integer deviceType, String productKey) { + if (deviceType == 1) { + return String.format("gateway_sub_" + productKey).toLowerCase(); + } else if (deviceType == 2) { + return String.format("gateway_" + productKey).toLowerCase(); + } else { + return String.format("device_" + productKey).toLowerCase(); + } + } + + /** + * 根据deviceId获取设备属性表名 + */ + static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { + return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase(); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java new file mode 100644 index 0000000000..b3ae6f8186 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java @@ -0,0 +1,141 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.domain.SelectDto; +import cn.iocoder.yudao.module.iot.domain.TableDto; +import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; + +import java.util.List; +import java.util.Map; + +/** + * TdEngineService + */ +public interface TdEngineService { + + /** + * 创建数据库 + * + * @param dataBaseName 数据库名称 + * @throws Exception 异常 + */ + void createDateBase(String dataBaseName) throws Exception; + + /** + * 创建超级表 + * + * @param schemaFields schema字段 + * @param tagsFields tags字段 + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @throws Exception 异常 + */ + void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, + String superTableName) throws Exception; + + /** + * 创建表 + * + * @param tableDto 表信息 + * @throws Exception 异常 + */ + void createTable(TableDto tableDto) throws Exception; + + /** + * 插入数据 + * + * @param tableDto 表信息 + * @throws Exception 异常 + */ + void insertData(TableDto tableDto) throws Exception; + + /** + * 根据时间戳查询数据 + * + * @param selectDto 查询条件 + * @return 数据 + * @throws Exception 异常 + */ + List> selectByTimesTamp(SelectDto selectDto) throws Exception; + + /** + * 为超级表添加列 + * + * @param superTableName 超级表名称 + * @param fieldsVo 字段信息 + * @throws Exception 异常 + */ + void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; + + /** + * 为超级表删除列 + * + * @param superTableName 超级表名称 + * @param fieldsVo 字段信息 + * @throws Exception 异常 + */ + void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; + + /** + * 为超级表添加tag + */ + Long getCountByTimesTamp(SelectDto selectDto) throws Exception; + + /** + * 检查表是否存在 + * + * @return 1存在 0不存在 + * @throws Exception 异常 + */ + Integer checkTableExists(SelectDto selectDto) throws Exception; + + /** + * 初始化超级表 + * + * @param msg 消息 + * @throws Exception 异常 + */ + void initSTableFrame(String msg) throws Exception; + + /** + * 获取最新数据 + * + * @param selectDto 查询条件 + * @return 数据 + * @throws Exception 异常 + */ + Map getLastData(SelectDto selectDto) throws Exception; + + /** + * 根据tag查询最新数据 + * + * @param tagsSelectDao 查询条件 + * @return 数据 + */ + Map> getLastDataByTags(TagsSelectDao tagsSelectDao); + + /** + * 获取历史数据 + * + * @param selectVisualDto 查询条件 + * @return 数据 + */ + List> getHistoryData(SelectVisualDto selectVisualDto); + + /** + * 获取实时数据 + * + * @param selectVisualDto 查询条件 + * @return 数据 + */ + List> getRealtimeData(SelectVisualDto selectVisualDto); + + /** + * 获取聚合数据 + * + * @param selectVisualDto 查询条件 + * @return 数据 + */ + List> getAggregateData(SelectVisualDto selectVisualDto); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java new file mode 100644 index 0000000000..70933617f7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.domain.SelectDto; +import cn.iocoder.yudao.module.iot.domain.TableDto; +import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@Service +@Slf4j +public class TdEngineServiceImpl implements TdEngineService { + + + @Override + public void createDateBase(String dataBaseName) { + + } + + @Override + public void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName) { + + } + + @Override + public void createTable(TableDto tableDto) { + + } + + @Override + public void insertData(TableDto tableDto) { + + } + + @Override + public List> selectByTimesTamp(SelectDto selectDto) { + return List.of(); + } + + @Override + public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { + + } + + @Override + public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { + + } + + @Override + public Long getCountByTimesTamp(SelectDto selectDto) { + return 0L; + } + + @Override + public Integer checkTableExists(SelectDto selectDto) { + return 0; + } + + @Override + public void initSTableFrame(String msg) { + + } + + @Override + public Map getLastData(SelectDto selectDto) { + return Map.of(); + } + + @Override + public Map> getLastDataByTags(TagsSelectDao tagsSelectDao) { + return Map.of(); + } + + @Override + public List> getHistoryData(SelectVisualDto selectVisualDto) { + return List.of(); + } + + @Override + public List> getRealtimeData(SelectVisualDto selectVisualDto) { + return List.of(); + } + + @Override + public List> getAggregateData(SelectVisualDto selectVisualDto) { + return List.of(); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java index ce8e472f59..3067772e66 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java @@ -62,4 +62,10 @@ public interface IotThinkModelFunctionService { */ PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO); + /** + * 创建超级表数据模型 + * + * @param productId 产品编号 + */ + void createSuperTableDataModel(Long productId); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 7937869443..1e356c6b7f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -15,10 +15,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper; import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; +import cn.iocoder.yudao.module.iot.service.tdengine.IotDbStructureDataService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -45,6 +48,11 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe @Resource private IotThinkModelFunctionMapper thinkModelFunctionMapper; + @Resource + private IotProductService productService; + @Resource + private IotDbStructureDataService dbStructureDataService; + @Override @Transactional(rollbackFor = Exception.class) public Long createThinkModelFunction(IotThinkModelFunctionSaveReqVO createReqVO) { @@ -162,6 +170,16 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe return thinkModelFunctionMapper.selectPage(pageReqVO); } + @Override + public void createSuperTableDataModel(Long productId) { + // 1. 查询产品 + IotProductDO product = productService.getProduct(productId); + // 2. 查询产品的物模型功能列表 + List functionList = thinkModelFunctionMapper.selectListByProductId(productId); + // 3. 生成 TDengine 的数据模型 + dbStructureDataService.createSuperTableDataModel(product, functionList); + } + /** * 创建默认的事件和服务 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml new file mode 100644 index 0000000000..f722e0130e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -0,0 +1,324 @@ + + + + + + + create database if not exists #{dataBaseName} + + + + create table if not exists #{dataBaseName}.#{superTableName} + + + ${item.fieldName} + + + + + timestamp + + + tinyint + + + smallint + + + int + + + bigint + + + float + + + double + + + binary + + + nchar + + + bool + + + json + + + + + (#{item.size}) + + + tags + + + + ${item.fieldName} + + + + + timestamp + + + tinyint + + + smallint + + + int + + + bigint + + + float + + + double + + + binary + + + nchar + + + bool + + + json + + + + + (#{item.size}) + + + + + + create table + if not exists #{dataBaseName}.#{tableName} + using #{dataBaseName}.#{superTableName} + tags + + #{item.fieldValue} + + + + + insert into #{dataBaseName}.#{tableName} + + #{item.fieldName} + + using #{dataBaseName}.#{superTableName} + tags + + #{item.fieldValue} + + values + + #{item.fieldValue} + + + + + + + + ALTER + STABLE + #{superTableName} + ADD + COLUMN + + #{fieldsVo.fieldName} + + + + + timestamp + + + tinyint + + + smallint + + + int + + + bigint + + + float + + + double + + + binary + + + nchar + + + bool + + + json + + + + + ( + #{fieldsVo.size} + ) + + + + + ALTER + STABLE + #{superTableName} + DROP + COLUMN + + #{fieldsVo.fieldName} + + + + + ALTER + STABLE + #{superTableName} + ADD + TAG + + #{fieldsVo.fieldName} + + + + + timestamp + + + tinyint + + + smallint + + + int + + + bigint + + + float + + + double + + + binary + + + nchar + + + bool + + + json + + + + + ( + #{fieldsVo.size} + ) + + + + + ALTER + STABLE + #{superTableName} + DROP + TAG + + #{fieldsVo.fieldName} + + + + + + + + + + + + + + + + + + ${sql} + + + From 61a0c05279613e6b9a95ef4ecc80cce7b2d3dc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 23 Oct 2024 23:26:24 +0800 Subject: [PATCH 002/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E9=9B=86=E6=88=90=20tdengine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/domain/BaseEntity.java | 7 ------- .../yudao/module/iot/domain/Fields.java | 7 ------- .../yudao/module/iot/domain/FieldsVo.java | 21 ------------------- .../iot/domain/ProductSuperTableModel.java | 12 +---------- .../yudao/module/iot/domain/SelectDto.java | 7 ------- .../module/iot/domain/SuperTableDto.java | 7 ------- .../yudao/module/iot/domain/TableDto.java | 7 ------- .../module/iot/domain/TagsSelectDao.java | 14 +++---------- 8 files changed, 4 insertions(+), 78 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java index 74a4c8494b..496be9a247 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java @@ -2,13 +2,6 @@ package cn.iocoder.yudao.module.iot.domain; import lombok.Data; -/** - * @ClassDescription: tdEngine的基础实体类 - * @ClassName: BaseEntity - * @Author: fxlinks - * @Date: 2021-12-30 14:39:25 - * @Version 1.0 - */ @Data public class BaseEntity { private static final long serialVersionUID = 1L; diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java index bbd7bb74e8..ec0a7f1483 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java @@ -2,13 +2,6 @@ package cn.iocoder.yudao.module.iot.domain; import lombok.Data; -/** - * @ClassDescription: 建表的字段实体类 - * @ClassName: Fields - * @Author: fxlinks - * @Date: 2021-12-28 09:09:04 - * @Version 1.0 - */ @Data public class Fields { private static final long serialVersionUID = 1L; diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java index 554d52149d..07aede29d3 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java @@ -6,13 +6,6 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -/** - * @ClassDescription: 建表的字段实体类的vo类 - * @ClassName: FieldsVo - * @Author: fxlinks - * @Date: 2021-12-28 11:30:06 - * @Version 1.0 - */ @Data public class FieldsVo { private static final long serialVersionUID = 1L; @@ -32,13 +25,6 @@ public class FieldsVo { */ private Integer size; - /** - * @param fields 字段实体类 - * @return FieldsVo 字段实体vo类 - * @MethodDescription 字段实体类转为vo类 - * @author fx - * @Date 2021/12/28 13:48 - */ public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException { // if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) { // throw new SQLException("invalid operation: fieldName or dataType can not be null"); @@ -51,13 +37,6 @@ public class FieldsVo { return null; } - /** - * @param fieldsList 字段实体类集合 - * @return List 字段实体vo类集合 - * @MethodDescription 字段实体类集合转为vo类集合 - * @author fx - * @Date 2021/12/28 14:00 - */ public static List fieldsTranscoding(List fieldsList) throws SQLException { List fieldsVoList = new ArrayList<>(); for (Fields fields : fieldsList) { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java index 49887415c7..c5989811fa 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java @@ -7,21 +7,11 @@ import java.sql.Timestamp; import java.util.HashMap; import java.util.Optional; -/** - * @Description: 产品超级表模型 - * @Author: fx - * @Website: http://www.sxfxck.com - * @CreateDate: 2022/1/1$ 19:37$ - * @UpdateUser: fx - * @UpdateDate: 2022/1/1$ 19:37$ - * @UpdateRemark: 修改内容 - * @Version: V1.0 - */ @Data public class ProductSuperTableModel { private static final long serialVersionUID = 1L; - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone = "GMT+8") private Timestamp ts; private String superTableName; diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java index 381ef3d6f1..c20e5ab5ee 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java @@ -4,13 +4,6 @@ import lombok.Data; import java.util.Set; -/** - * @ClassDescription: 查询所需入参对象 - * @ClassName: SelectDto - * @Author: fxlinks - * @Date: 2022-01-07 14:12:26 - * @Version 1.0 - */ @Data public class SelectDto { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java index ee80d42cfd..a15f135cfb 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java @@ -4,13 +4,6 @@ import lombok.Data; import java.util.List; -/** - * @ClassDescription: 创建超级表需要的入参的实体类 - * @ClassName: SuperTableDto - * @Author: fxlinks - * @Date: 2021-12-28 15:03:45 - * @Version 1.0 - */ @Data public class SuperTableDto extends BaseEntity { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java index f27beb1a69..5ffe9f84e4 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java @@ -4,13 +4,6 @@ import lombok.Data; import java.util.List; -/** - * @ClassDescription: 创建超级表的子表需要的入参的实体类 - * @ClassName: TableDto - * @Author: fxlinks - * @Date: 2021-12-30 14:42:47 - * @Version 1.0 - */ @Data public class TableDto extends BaseEntity { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java index 5cb9c5ed92..8833b0b50b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java @@ -3,24 +3,16 @@ package cn.iocoder.yudao.module.iot.domain; import lombok.Data; -/** - * @program: fxlinks - * @description: 标签查询模型 - * @packagename: com.fx.tdengine.api.domain.rule - * @author: fx - * @e-mainl: 13733918655@163.com - * @date: 2022-07-27 18:40 - **/ @Data public class TagsSelectDao { -// @NotBlank(message = "invalid operation: dataBaseName can not be empty") + // @NotBlank(message = "invalid operation: dataBaseName can not be empty") private String dataBaseName; -// @NotBlank(message = "invalid operation: stableName can not be empty") + // @NotBlank(message = "invalid operation: stableName can not be empty") private String stableName; -// @NotBlank(message = "invalid operation: tagsName can not be empty") + // @NotBlank(message = "invalid operation: tagsName can not be empty") private String tagsName; // @NotNull(message = "invalid operation: startTime can not be null") From ea8dd67e9ecc34e7e8af3402f00c819d53f5130a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 23 Oct 2024 23:38:55 +0800 Subject: [PATCH 003/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E5=A2=9E=E5=8A=A0=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/vo/IotDevicePageReqVO.java | 1 - .../admin/device/vo/IotDeviceRespVO.java | 1 - .../thingModel/ThingModelRespVO.java | 1 - .../vo/IotThinkModelFunctionRespVO.java | 1 - .../dal/dataobject/tdengine/FieldParser.java | 37 ++++++++++--------- .../dal/dataobject/tdengine/TableData.java | 3 ++ .../dal/dataobject/tdengine/TableManager.java | 3 ++ .../dal/dataobject/tdengine/TdResponse.java | 5 ++- .../dal/dataobject/tdengine/TdRestApi.java | 3 ++ 9 files changed, 33 insertions(+), 22 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java index 26bdaca05e..1c29f9f810 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java @@ -10,7 +10,6 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.math.BigDecimal; import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java index 488f6b9079..0b2cf25f56 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java @@ -5,7 +5,6 @@ import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import java.math.BigDecimal; import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 设备 Response VO") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java index 6fc48ef4f7..d7478e54a4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMod import lombok.*; import java.util.List; -import java.util.Map; @Data @Builder diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java index 9ef3f58d8b..e2f3d2ddc6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java @@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -import java.util.List; @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 0a91e7cf19..e045e5ea3c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType; @@ -9,21 +8,26 @@ import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; +/** + * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 + * + */ public class FieldParser { /** * 物模型到td数据类型映射 */ - private static final HashMap TYPE_MAPPING = new HashMap<>() {{ - put("INT", "INT"); - put("FLOAT", "FLOAT"); - put("DOUBLE", "DOUBLE"); - put("BOOL", "BOOL"); - put("ENUM", "NCHAR"); - put("TEXT", "NCHAR"); - put("DATE", "NCHAR"); - }}; - + private static final HashMap TYPE_MAPPING = new HashMap<>() { + { + put("INT", "INT"); + put("FLOAT", "FLOAT"); + put("DOUBLE", "DOUBLE"); + put("BOOL", "BOOL"); + put("ENUM", "NCHAR"); + put("TEXT", "NCHAR"); + put("DATE", "NCHAR"); + } + }; /** * 将物模型字段转换为td字段 @@ -57,9 +61,8 @@ public class FieldParser { /** * 将从库中查出来的字段信息转换为td字段对象 */ - public static List parse(List rows) { - return (List) rows.stream().map((r) -> { - List row = (List) r; + public static List parse(List> rows) { + return rows.stream().map(row -> { String type = row.get(1).toString().toUpperCase(); return new TdField( row.get(0).toString(), @@ -72,9 +75,9 @@ public class FieldParser { * 获取字段字义 */ public static String getFieldDefine(TdField field) { - return "`" + field.getName() + "`" + " " + (field.getLength() > 0 ? - String.format("%s(%d)", field.getType(), field.getLength()) - : field.getType()); + return "`" + field.getName() + "`" + " " + + (field.getLength() > 0 ? String.format("%s(%d)", field.getType(), field.getLength()) + : field.getType()); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java index 1d42933d4c..5c3c585e7f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java @@ -4,6 +4,9 @@ import lombok.Data; import java.util.List; +/** + * TableData 类用于存储和操作 TDengine 表数据 + */ @Data public class TableData { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java index 1bf4ecf646..b2762f6ed3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import java.util.List; +/** + * TableManager 类用于管理 TDengine 表的创建、删除和结构信息获取 + */ public class TableManager { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java index 380807a1b8..9353565be8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java @@ -6,6 +6,9 @@ import lombok.NoArgsConstructor; import java.util.List; +/** + * TdResponse 类用于处理 TDengine 的响应 + */ @Data @NoArgsConstructor @AllArgsConstructor @@ -21,6 +24,6 @@ public class TdResponse { private String desc; //[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] - private List data; + private List data; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index 4506383f61..ef6b8b5b41 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -7,6 +7,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +/** + * TdRestApi 类用于处理 TDengine 的 REST API 请求 + */ @Slf4j @Service public class TdRestApi { From 7b5aa23d5cdc55779ff3048d6cc2e04e82b24b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 26 Oct 2024 23:15:31 +0800 Subject: [PATCH 004/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E4=BA=A7=E5=93=81=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E8=B6=85=E7=BA=A7=E8=A1=A8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/domain/FieldsVo.java | 26 +- .../dal/dataobject/tdengine/FieldParser.java | 44 +-- .../dal/dataobject/tdengine/TableData.java | 2 +- .../dal/dataobject/tdengine/TableManager.java | 26 +- .../tdengine/{TdField.java => TdFieldDO.java} | 10 +- .../dal/dataobject/tdengine/TdResponse.java | 18 +- .../dal/dataobject/tdengine/TdRestApi.java | 23 +- .../iot/dal/tdengine/TdEngineMapper.java | 87 ++++-- .../IotDbStructureDataServiceImpl.java | 282 +++++++++++------- .../iot/service/tdengine/TdEngineService.java | 75 ++--- .../service/tdengine/TdEngineServiceImpl.java | 44 ++- .../IotThinkModelFunctionServiceImpl.java | 2 + .../mapper/tdengine/TdEngineMapper.xml | 223 +++++++------- 13 files changed, 491 insertions(+), 371 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/{TdField.java => TdFieldDO.java} (68%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java index 07aede29d3..fc86a8f7ee 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java @@ -1,14 +1,18 @@ package cn.iocoder.yudao.module.iot.domain; +import lombok.Builder; import lombok.Data; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +/** + * 字段信息 VO + */ @Data +@Builder public class FieldsVo { - private static final long serialVersionUID = 1L; /** * 字段名称 @@ -24,24 +28,4 @@ public class FieldsVo { * 字段字节大小 */ private Integer size; - - public static FieldsVo fieldsTranscoding(Fields fields) throws SQLException { -// if (StringUtils.isBlank(fields.getFieldName()) || fields.getDataType() == null) { -// throw new SQLException("invalid operation: fieldName or dataType can not be null"); -// } -// FieldsVo fieldsVo = new FieldsVo(); -// fieldsVo.setFieldName(fields.getFieldName()); -// fieldsVo.setDataType(fields.getDataType().getDataType()); -// fieldsVo.setSize(fields.getSize()); -// return fieldsVo; - return null; - } - - public static List fieldsTranscoding(List fieldsList) throws SQLException { - List fieldsVoList = new ArrayList<>(); - for (Fields fields : fieldsList) { - fieldsVoList.add(fieldsTranscoding(fields)); - } - return fieldsVoList; - } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index e045e5ea3c..344d65c9a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -10,7 +10,6 @@ import java.util.stream.Collectors; /** * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 - * */ public class FieldParser { @@ -35,49 +34,58 @@ public class FieldParser { * @param property 物模型属性 * @return TdField对象 */ - public static TdField parse(ThingModelProperty property) { + public static TdFieldDO parse(ThingModelProperty property) { String fieldName = property.getIdentifier().toLowerCase(); ThingModelDataType type = property.getDataType(); // 将物模型字段类型映射为td字段类型 String fType = TYPE_MAPPING.get(type.getType().toUpperCase()); - int len = -1; // 如果字段类型为NCHAR,默认长度为64 + int dataLength = 0; if ("NCHAR".equals(fType)) { - len = 64; + dataLength = 64; } - - return new TdField(fieldName, fType, len); + return new TdFieldDO(fieldName, fType, dataLength); } /** - * 获取物模型中的字段列表 + * 从物模型中获取字段列表 + * + * @param thingModel 物模型响应对象 + * @return 字段列表 */ - public static List parse(ThingModelRespVO thingModel) { - return thingModel.getModel().getProperties().stream().map(FieldParser::parse).collect(Collectors.toList()); + public static List parse(ThingModelRespVO thingModel) { + return thingModel.getModel().getProperties().stream() + .map(FieldParser::parse) + .collect(Collectors.toList()); } /** - * 将从库中查出来的字段信息转换为td字段对象 + * 将从库中查出来的字段信息转换为 TDengine 字段对象 + * + * @param rows 从库中查出的字段信息列表 + * @return 转换后的 TDengine 字段对象列表 */ - public static List parse(List> rows) { + public static List parse(List> rows) { return rows.stream().map(row -> { String type = row.get(1).toString().toUpperCase(); - return new TdField( + int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; + return new TdFieldDO( row.get(0).toString(), type, - type.equals("NCHAR") ? Integer.parseInt(row.get(2).toString()) : -1); + dataLength + ); }).collect(Collectors.toList()); } /** * 获取字段字义 */ - public static String getFieldDefine(TdField field) { - return "`" + field.getName() + "`" + " " - + (field.getLength() > 0 ? String.format("%s(%d)", field.getType(), field.getLength()) - : field.getType()); + public static String getFieldDefine(TdFieldDO field) { + return "`" + field.getFieldName() + "`" + " " + + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) + : field.getDataType()); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java index 5c3c585e7f..e9acbb7f0a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java @@ -36,4 +36,4 @@ public class TableData { * 超级表名称 */ private String superTableName; -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java index b2762f6ed3..65663d8053 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java @@ -45,7 +45,7 @@ public class TableManager { /** * 获取创建表sql */ - public static String getCreateSTableSql(String tbName, List fields, TdField... tags) { + public static String getCreateSTableSql(String tbName, List fields, TdFieldDO... tags) { if (fields.isEmpty()) { return null; } @@ -53,7 +53,7 @@ public class TableManager { // 生成字段片段 StringBuilder sbField = new StringBuilder("time TIMESTAMP,"); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbField.append(FieldParser.getFieldDefine(field)); sbField.append(","); } @@ -63,7 +63,7 @@ public class TableManager { // 生成tag StringBuilder sbTag = new StringBuilder(); - for (TdField tag : tags) { + for (TdFieldDO tag : tags) { sbTag.append(FieldParser.getFieldDefine(tag)) .append(","); } @@ -76,7 +76,7 @@ public class TableManager { /** * 获取创建普通表sql */ - public static String getCreateCTableSql(String tbName, List fields) { + public static String getCreateCTableSql(String tbName, List fields) { if (fields.size() == 0) { return null; } @@ -84,7 +84,7 @@ public class TableManager { //生成字段片段 StringBuilder sbField = new StringBuilder("time timestamp,"); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbField.append(FieldParser.getFieldDefine(field)); sbField.append(","); } @@ -116,9 +116,9 @@ public class TableManager { /** * 获取添加字段sql */ - public static String getAddSTableColumnSql(String tbName, List fields) { + public static String getAddSTableColumnSql(String tbName, List fields) { StringBuilder sbAdd = new StringBuilder(); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL, tbName, FieldParser.getFieldDefine(field) @@ -130,9 +130,9 @@ public class TableManager { /** * 获取修改字段sql */ - public static String getModifySTableColumnSql(String tbName, List fields) { + public static String getModifySTableColumnSql(String tbName, List fields) { StringBuilder sbModify = new StringBuilder(); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL, tbName, FieldParser.getFieldDefine(field) @@ -144,15 +144,15 @@ public class TableManager { /** * 获取删除字段sql */ - public static String getDropSTableColumnSql(String tbName, List fields) { + public static String getDropSTableColumnSql(String tbName, List fields) { StringBuilder sbDrop = new StringBuilder(); - for (TdField field : fields) { + for (TdFieldDO field : fields) { sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL, tbName, - field.getName() + field.getFieldName() )); } return sbDrop.toString(); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index f040a017ad..d9cf28f1bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdField.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,20 +11,21 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor -public class TdField { +@Builder +public class TdFieldDO { /** * 字段名称 */ - private String name; + private String fieldName; /** * 字段类型 */ - private String type; + private String dataType; /** * 字段长度 */ - private int length; + private Integer dataLength = 0; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java index 9353565be8..aca5cece5b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java @@ -17,13 +17,25 @@ public class TdResponse { public static final int CODE_SUCCESS = 0; public static final int CODE_TB_NOT_EXIST = 866; + /** + * 状态 + */ private String status; + /** + * 错误码 + */ private int code; + /** + * 错误信息 + */ private String desc; - //[["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] - private List data; + /** + * 列信息 + * [["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] + */ + private List data; -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index ef6b8b5b41..6653ece40e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -14,23 +14,23 @@ import org.springframework.stereotype.Service; @Service public class TdRestApi { - @Value("${spring.datasource.dynamic.datasource.master.url}") + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; - @Value("${spring.datasource.dynamic.datasource.master.username}") + @Value("${spring.datasource.dynamic.datasource.tdengine.username}") private String username; - @Value("${spring.datasource.dynamic.datasource.master.password}") + @Value("${spring.datasource.dynamic.datasource.tdengine.password}") private String password; + /** + * 获取 REST API URL + */ private String getRestApiUrl() { - //jdbc:TAOS-RS://127.0.0.1:6041/iotkit?xxxx - String restUrl = url.replace("jdbc:TAOS-RS://" , "") - .replaceAll("\\?.*" , ""); - // /rest/sql/iotkit + String restUrl = url.replace("jdbc:TAOS-RS://", "") + .replaceAll("\\?.*", ""); int idx = restUrl.lastIndexOf("/"); - //127.0.0.1:6041/rest/sql/iotkit - return String.format("%s/rest/sql/%s" , restUrl.substring(0, idx), restUrl.substring(idx + 1)); + return String.format("%s/rest/sql/%s", restUrl.substring(0, idx), restUrl.substring(idx + 1)); } @@ -48,11 +48,10 @@ public class TdRestApi { * 执行sql */ public TdResponse execSql(String sql) { - log.info("exec td sql:{}" , sql); + log.info("exec td sql:{}", sql); HttpRequest request = newApiRequest(sql); HttpResponse response = request.execute(); return JSONUtil.toBean(response.body(), TdResponse.class); } - -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java index 6a925f4e5a..85e324a77c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.domain.FieldsVo; import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.TableDto; @@ -17,24 +18,80 @@ import java.util.Map; @DS("tdengine") public interface TdEngineMapper { + /** + * 创建数据库 + * + * @param dataBaseName 数据库名称 + */ + @InterceptorIgnore(tenantLine = "true") void createDatabase(@Param("dataBaseName") String dataBaseName); - void createSuperTable(@Param("schemaFields") List schemaFields, - @Param("tagsFields") List tagsFields, + /** + * 创建超级表 + * + * @param schemaFields schema字段 + * @param tagsFields tags字段 + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + void createSuperTable(@Param("schemaFields") List schemaFields, + @Param("tagsFields") List tagsFields, @Param("dataBaseName") String dataBaseName, @Param("superTableName") String superTableName); + /** + * 查看超级表 - 显示当前数据库下的所有超级表信息 + * SQL:SHOW STABLES [LIKE tb_name_wildcard]; + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + List> showSuperTables(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName); + + /** + * 查看超级表 - 获取超级表的结构信息 + * SQL:DESCRIBE [db_name.]stb_name; + *

+ * * @param dataBaseName 数据库名称 + * * @param superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + List> describeSuperTable(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName); + + /** + * 为超级表添加列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param field 字段信息 + */ + @InterceptorIgnore(tenantLine = "true") + void addColumnForSuperTable(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName, + @Param("field") TdFieldDO field); + + /** + * 为超级表删除列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param field 字段信息 + */ + @InterceptorIgnore(tenantLine = "true") + void dropColumnForSuperTable(@Param("dataBaseName") String dataBaseName, + @Param("superTableName") String superTableName, + @Param("field") TdFieldDO field); + void createTable(TableDto tableDto); void insertData(TableDto tableDto); List> selectByTimestamp(SelectDto selectDto); - void addColumnForSuperTable(@Param("superTableName") String superTableName, - @Param("fieldsVo") FieldsVo fieldsVo); - - void dropColumnForSuperTable(@Param("superTableName") String superTableName, - @Param("fieldsVo") FieldsVo fieldsVo); void addTagForSuperTable(@Param("superTableName") String superTableName, @Param("fieldsVo") FieldsVo fieldsVo); @@ -44,14 +101,6 @@ public interface TdEngineMapper { Map getCountByTimestamp(SelectDto selectDto); - /** - * 检查表是否存在 - * - * @param dataBaseName 数据库名称 - * @param tableName 表名称 - */ - Integer checkTableExists(@Param("dataBaseName") String dataBaseName, @Param("tableName") String tableName); - Map getLastData(SelectDto selectDto); List> getHistoryData(SelectVisualDto selectVisualDto); @@ -62,13 +111,5 @@ public interface TdEngineMapper { List> getLastDataByTags(TagsSelectDao tagsSelectDao); - /** - * 创建超级表 - * - * @param sql sql - * @return 返回值 - */ - @InterceptorIgnore(tenantLine = "true") - Integer createSuperTableDevice(String sql); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java index f8dd6feb5d..d42fd72fc9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java @@ -1,19 +1,22 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.hutool.json.JSONUtil; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdRestApi; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -22,147 +25,206 @@ import java.util.stream.Collectors; public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { @Resource - private TdEngineMapper tdEngineMapper; + private TdEngineService tdEngineService; @Resource private TdRestApi tdRestApi; + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") + private String url; + @Override public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - // 获取物模型中的属性定义 - List fields = FieldParser.parse(thingModel); - String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); + // 1. 解析物模型,获得字段列表 + List schemaFields = new ArrayList<>(); + schemaFields.add(TdFieldDO.builder(). + fieldName("time"). + dataType("TIMESTAMP"). + build()); + schemaFields.addAll(FieldParser.parse(thingModel)); - // 生成创建超级表的 SQL - String sql = TableManager.getCreateSTableSql(tbName, fields, new TdField("device_id", "NCHAR", 64)); - if (sql == null) { - log.warn("生成的 SQL 为空,无法创建超级表"); - return; - } - log.info("执行 SQL: {}", sql); + // 3. 设置超级表的标签 + List tagsFields = new ArrayList<>(); + tagsFields.add(TdFieldDO.builder(). + fieldName("product_key"). + dataType("NCHAR"). + dataLength(64). + build()); + tagsFields.add(TdFieldDO.builder(). + fieldName("device_key"). + dataType("NCHAR"). + dataLength(64). + build()); + tagsFields.add(TdFieldDO.builder(). + fieldName("device_name"). + dataType("NCHAR"). + dataLength(64). + build()); + // 年 + tagsFields.add(TdFieldDO.builder(). + fieldName("year"). + dataType("INT"). + build()); + // 月 + tagsFields.add(TdFieldDO.builder(). + fieldName("month"). + dataType("INT"). + build()); + // 日 + tagsFields.add(TdFieldDO.builder(). + fieldName("day"). + dataType("INT"). + build()); + // 时 + tagsFields.add(TdFieldDO.builder(). + fieldName("hour"). + dataType("INT"). + build()); - // 执行 SQL 创建超级表 - tdEngineMapper.createSuperTableDevice(sql); + + // 4. 获取超级表的名称 + String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); + + // 5. 创建超级表 + String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + tdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); } @Override public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { try { - // 获取旧字段信息 String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); - String sql = TableManager.getDescTableSql(tbName); - TdResponse response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("获取表描述错误: " + JSONUtil.toJsonStr(response)); - } + List oldFields = getTableFields(tbName); + List newFields = FieldParser.parse(thingModel); - List oldFields = FieldParser.parse(response.getData()); - List newFields = FieldParser.parse(thingModel); - - // 找出新增的字段 - List addFields = newFields.stream() - .filter(f -> oldFields.stream().noneMatch(old -> old.getName().equals(f.getName()))) - .collect(Collectors.toList()); - if (!addFields.isEmpty()) { - sql = TableManager.getAddSTableColumnSql(tbName, addFields); - response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("添加表字段错误: " + JSONUtil.toJsonStr(response)); - } - } - - // 找出修改的字段 - List modifyFields = newFields.stream() - .filter(f -> oldFields.stream().anyMatch(old -> - old.getName().equals(f.getName()) && - (!old.getType().equals(f.getType()) || old.getLength() != f.getLength()))) - .collect(Collectors.toList()); - if (!modifyFields.isEmpty()) { - sql = TableManager.getModifySTableColumnSql(tbName, modifyFields); - response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("修改表字段错误: " + JSONUtil.toJsonStr(response)); - } - } - - // 找出删除的字段 - List dropFields = oldFields.stream() - .filter(f -> !"time".equals(f.getName()) && !"device_id".equals(f.getName()) && - newFields.stream().noneMatch(n -> n.getName().equals(f.getName()))) - .collect(Collectors.toList()); - if (!dropFields.isEmpty()) { - sql = TableManager.getDropSTableColumnSql(tbName, dropFields); - response = tdRestApi.execSql(sql); - if (response.getCode() != TdResponse.CODE_SUCCESS) { - throw new RuntimeException("删除表字段错误: " + JSONUtil.toJsonStr(response)); - } - } + updateTableFields(tbName, oldFields, newFields); } catch (Throwable e) { log.error("更新物模型超级表失败", e); } } + // 获取表字段 + private List getTableFields(String tableName) { + List fields = new ArrayList<>(); + // 获取超级表的描述信息 + List> maps = tdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName); + if (maps != null) { + // 过滤掉 note 字段为 TAG 的记录 + maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList(); + // 过滤掉 time 字段 + maps = maps.stream().filter(map -> !"time".equals(map.get("field"))).toList(); + // 解析字段信息 + fields = FieldParser.parse(maps.stream() + .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) + .collect(Collectors.toList())); + } + return fields; + } + + // 更新表字段 + private void updateTableFields(String tableName, List oldFields, List newFields) { + // 获取新增字段 + List addFields = getAddFields(oldFields, newFields); + // 获取修改字段 + List modifyFields = getModifyFields(oldFields, newFields); + // 获取删除字段 + List dropFields = getDropFields(oldFields, newFields); + + String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + // 添加新增字段 + if (CollUtil.isNotEmpty(addFields)) { + tdEngineService.addColumnForSuperTable(dataBaseName,tableName, addFields); + } + // 删除旧字段 + if (CollUtil.isNotEmpty(dropFields)) { + tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, dropFields); + } + // 修改字段(先删除再添加) + if (CollUtil.isNotEmpty(modifyFields)) { + tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, modifyFields); + tdEngineService.addColumnForSuperTable(dataBaseName,tableName, modifyFields); + } + } + + // 获取新增字段 + private List getAddFields(List oldFields, List newFields) { + return newFields.stream() + .filter(f -> oldFields.stream().noneMatch(old -> old.getFieldName().equals(f.getFieldName()))) + .collect(Collectors.toList()); + } + + // 获取修改字段 + private List getModifyFields(List oldFields, List newFields) { + return newFields.stream() + .filter(f -> oldFields.stream().anyMatch(old -> + old.getFieldName().equals(f.getFieldName()) && + (!old.getDataType().equals(f.getDataType()) || !Objects.equals(old.getDataLength(), f.getDataLength())))) + .collect(Collectors.toList()); + } + + // 获取删除字段 + private List getDropFields(List oldFields, List newFields) { + return oldFields.stream() + .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()) && + newFields.stream().noneMatch(n -> n.getFieldName().equals(f.getFieldName()))) + .collect(Collectors.toList()); + } + @Override public void createSuperTableDataModel(IotProductDO product, List functionList) { - // 1. 生成 ThingModelRespVO - ThingModelRespVO thingModel = new ThingModelRespVO(); - thingModel.setId(product.getId()); - thingModel.setProductKey(product.getProductKey()); + ThingModelRespVO thingModel = buildThingModel(product, functionList); - // 1.1 设置属性、服务和事件 - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); - List properties = new ArrayList<>(); - - // 1.2 遍历功能列表并分类 - for (IotThinkModelFunctionDO function : functionList) { - if (Objects.requireNonNull(IotProductFunctionTypeEnum.valueOf(function.getType())) == IotProductFunctionTypeEnum.PROPERTY) { - ThingModelProperty property = new ThingModelProperty(); - property.setIdentifier(function.getIdentifier()); - property.setName(function.getName()); - property.setDescription(function.getDescription()); - property.setDataType(function.getProperty().getDataType()); - properties.add(property); - } - } - - // 1.3 判断属性列表是否为空 - if (properties.isEmpty()) { + if (thingModel.getModel().getProperties().isEmpty()) { log.warn("物模型属性列表为空,不创建超级表"); return; } - model.setProperties(properties); - thingModel.setModel(model); + String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); + String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + Integer tableExists = tdEngineService.checkSuperTableExists(dataBaseName, superTableName); - // 2. 判断是否已经创建,如果已经创建则进行更新 - String tbName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); - Integer iot = tdEngineMapper.checkTableExists("ruoyi_vue_pro", tbName); - if (iot != null && iot > 0) { - // 3. 更新 + if (tableExists != null && tableExists > 0) { updateSuperTable(thingModel, product.getDeviceType()); } else { - // 4. 创建 createSuperTable(thingModel, product.getDeviceType()); } } - /** - * 根据产品key获取产品属性超级表名 - */ - static String getProductPropertySTableName(Integer deviceType, String productKey) { - if (deviceType == 1) { - return String.format("gateway_sub_" + productKey).toLowerCase(); - } else if (deviceType == 2) { - return String.format("gateway_" + productKey).toLowerCase(); - } else { - return String.format("device_" + productKey).toLowerCase(); - } + private ThingModelRespVO buildThingModel(IotProductDO product, List functionList) { + ThingModelRespVO thingModel = new ThingModelRespVO(); + thingModel.setId(product.getId()); + thingModel.setProductKey(product.getProductKey()); + + ThingModelRespVO.Model model = new ThingModelRespVO.Model(); + List properties = functionList.stream() + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(IotProductFunctionTypeEnum.valueOf(function.getType()))) + .map(this::buildThingModelProperty) + .collect(Collectors.toList()); + + model.setProperties(properties); + thingModel.setModel(model); + + return thingModel; } - /** - * 根据deviceId获取设备属性表名 - */ - static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { - return String.format(deviceType + "_" + productKey + "_" + deviceKey).toLowerCase(); + private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) { + ThingModelProperty property = new ThingModelProperty(); + property.setIdentifier(function.getIdentifier()); + property.setName(function.getName()); + property.setDescription(function.getDescription()); + property.setDataType(function.getProperty().getDataType()); + return property; } -} + + static String getProductPropertySTableName(Integer deviceType, String productKey) { + return switch (deviceType) { + case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); + case 2 -> String.format("gateway_%s", productKey).toLowerCase(); + default -> String.format("device_%s", productKey).toLowerCase(); + }; + } + + static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { + return String.format("%s_%s_%s", deviceType, productKey, deviceKey).toLowerCase(); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java index b3ae6f8186..9319adbd77 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; @@ -27,12 +27,44 @@ public interface TdEngineService { * * @param schemaFields schema字段 * @param tagsFields tags字段 - * @param dataBaseName 数据库名称 * @param superTableName 超级表名称 - * @throws Exception 异常 */ - void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, - String superTableName) throws Exception; + void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName); + + /** + * 检查超级表是否存在 + */ + Integer checkSuperTableExists(String dataBaseName, String superTableName); + + + /** + * 获取超级表的结构信息 + */ + List> describeSuperTable(String dataBaseName, String superTableName); + + /** + * 为超级表添加列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param fieldsVo 字段信息 + */ + void addColumnForSuperTable(String dataBaseName,String superTableName, List fieldsVo); + + /** + * 为超级表删除列 + * + * @param dataBaseName 数据库名称 + * @param superTableName 超级表名称 + * @param fieldsVo 字段信息 + */ + void dropColumnForSuperTable(String dataBaseName,String superTableName, List fieldsVo); + + /** + * 为超级表添加tag + */ + Long getCountByTimesTamp(SelectDto selectDto) throws Exception; + /** * 创建表 @@ -59,37 +91,6 @@ public interface TdEngineService { */ List> selectByTimesTamp(SelectDto selectDto) throws Exception; - /** - * 为超级表添加列 - * - * @param superTableName 超级表名称 - * @param fieldsVo 字段信息 - * @throws Exception 异常 - */ - void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; - - /** - * 为超级表删除列 - * - * @param superTableName 超级表名称 - * @param fieldsVo 字段信息 - * @throws Exception 异常 - */ - void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) throws Exception; - - /** - * 为超级表添加tag - */ - Long getCountByTimesTamp(SelectDto selectDto) throws Exception; - - /** - * 检查表是否存在 - * - * @return 1存在 0不存在 - * @throws Exception 异常 - */ - Integer checkTableExists(SelectDto selectDto) throws Exception; - /** * 初始化超级表 * @@ -138,4 +139,6 @@ public interface TdEngineService { * @return 数据 */ List> getAggregateData(SelectVisualDto selectVisualDto); + + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java index 70933617f7..d61b789222 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.iocoder.yudao.module.iot.domain.FieldsVo; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; import cn.iocoder.yudao.module.iot.domain.SelectDto; import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -15,25 +17,27 @@ import java.util.Map; @Slf4j public class TdEngineServiceImpl implements TdEngineService { + @Resource + private TdEngineMapper tdEngineMapper; @Override public void createDateBase(String dataBaseName) { - + tdEngineMapper.createDatabase(dataBaseName); } @Override - public void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName) { - + public void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName) { + tdEngineMapper.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); } @Override public void createTable(TableDto tableDto) { - + tdEngineMapper.createTable(tableDto); } @Override public void insertData(TableDto tableDto) { - + tdEngineMapper.insertData(tableDto); } @Override @@ -42,13 +46,17 @@ public class TdEngineServiceImpl implements TdEngineService { } @Override - public void addColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { - + public void addColumnForSuperTable(String dataBaseName,String superTableName, List fields) { + for (TdFieldDO field : fields) { + tdEngineMapper.addColumnForSuperTable(dataBaseName,superTableName, field); + } } @Override - public void dropColumnForSuperTable(String superTableName, FieldsVo fieldsVo) { - + public void dropColumnForSuperTable(String dataBaseName,String superTableName, List fields) { + for (TdFieldDO field : fields) { + tdEngineMapper.dropColumnForSuperTable(dataBaseName,superTableName, field); + } } @Override @@ -56,11 +64,6 @@ public class TdEngineServiceImpl implements TdEngineService { return 0L; } - @Override - public Integer checkTableExists(SelectDto selectDto) { - return 0; - } - @Override public void initSTableFrame(String msg) { @@ -90,4 +93,15 @@ public class TdEngineServiceImpl implements TdEngineService { public List> getAggregateData(SelectVisualDto selectVisualDto) { return List.of(); } + + @Override + public Integer checkSuperTableExists(String dataBaseName, String superTableName) { + List> results = tdEngineMapper.showSuperTables(dataBaseName, superTableName); + return results == null || results.isEmpty() ? 0 : results.size(); + } + + @Override + public List> describeSuperTable(String dataBaseName, String superTableName) { + return tdEngineMapper.describeSuperTable(dataBaseName, superTableName); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 1e356c6b7f..3f698999c7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -174,8 +174,10 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe public void createSuperTableDataModel(Long productId) { // 1. 查询产品 IotProductDO product = productService.getProduct(productId); + // 2. 查询产品的物模型功能列表 List functionList = thinkModelFunctionMapper.selectListByProductId(productId); + // 3. 生成 TDengine 的数据模型 dbStructureDataService.createSuperTableDataModel(product, functionList); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml index f722e0130e..f1d7481981 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -8,7 +8,7 @@ - create table if not exists #{dataBaseName}.#{superTableName} + CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName} @@ -16,46 +16,48 @@ - - timestamp + + TIMESTAMP - - tinyint + + TINYINT - - smallint + + SMALLINT - - int + + INT - - bigint + + BIGINT - - float + + FLOAT - - double + + DOUBLE - - binary + + BINARY - - nchar + + NCHAR - - bool + + BOOL - - json + + JSON - - (#{item.size}) + + ( + ${item.dataLength} + ) - tags + TAGS @@ -64,43 +66,45 @@ - - timestamp + + TIMESTAMP - - tinyint + + TINYINT - - smallint + + SMALLINT - - int + + INT - - bigint + + BIGINT - - float + + FLOAT - - double + + DOUBLE - - binary + + BINARY - - nchar + + NCHAR - - bool + + BOOL - - json + + JSON - - (#{item.size}) + + ( + ${item.dataLength} + ) @@ -135,7 +139,6 @@ - - ALTER - STABLE - #{superTableName} - ADD - COLUMN - - #{fieldsVo.fieldName} + ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN + + #{field.fieldName} - + - - timestamp + + TIMESTAMP - - tinyint + + TINYINT - - smallint + + SMALLINT - - int + + INT - - bigint + + BIGINT - - float + + FLOAT - - double + + DOUBLE - - binary + + BINARY - - nchar + + NCHAR - - bool + + BOOL - - json + + JSON - + ( - #{fieldsVo.size} + #{field.dataLength} ) - ALTER - STABLE - #{superTableName} - DROP - COLUMN - - #{fieldsVo.fieldName} + ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN + + #{field.fieldName} @@ -215,49 +210,49 @@ #{superTableName} ADD TAG - - #{fieldsVo.fieldName} + + #{fieldDO.fieldName} - + - + timestamp - + tinyint - + smallint - + int - + bigint - + float - + double - + binary - + nchar - + bool - + json - + ( - #{fieldsVo.size} + #{fieldDO.dataLength} ) @@ -279,11 +274,8 @@ FROM #{dataBaseName}.#{tableName} WHERE ${fieldName} BETWEEN #{startTime} AND #{endTime} - + SHOW ${dataBaseName}.STABLES LIKE '${superTableName}' + + - - - ${sql} - + From 88088c798799bfebd00439199914f2fb08a7a8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 26 Oct 2024 23:26:26 +0800 Subject: [PATCH 005/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=20=E4=BA=A7=E5=93=81=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E5=90=8E=E7=89=A9=E6=A8=A1=E5=9E=8B=E4=B8=8D=E5=8F=AF=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 1 + .../IotThinkModelFunctionServiceImpl.java | 25 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 31702e1e61..be9d28ecb0 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -13,6 +13,7 @@ public interface ErrorCodeConstants { ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_050_001_000, "产品不存在"); ErrorCode PRODUCT_IDENTIFICATION_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); + ErrorCode PRODUCT_STATUS_NOT_ALLOW_FUNCTION = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); // ========== IoT 产品物模型 1-050-002-000 ============ ErrorCode THINK_MODEL_FUNCTION_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 3f698999c7..dd7a51786b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkMod import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper; import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotDbStructureDataService; import jakarta.annotation.Resource; @@ -65,17 +66,27 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe // 3. 系统保留字段,不能用于标识符定义 validateNotDefaultEventAndService(createReqVO.getIdentifier()); - // 3. 插入数据库 + // 4. 校验产品状态,发布状态下,不允许新增功能 + validateProductStatus(createReqVO.getProductId()); + + // 5. 插入数据库 IotThinkModelFunctionDO function = IotThinkModelFunctionConvert.INSTANCE.convert(createReqVO); thinkModelFunctionMapper.insert(function); - // 4. 如果创建的是属性,需要更新默认的事件和服务 + // 6. 如果创建的是属性,需要更新默认的事件和服务 if (Objects.equals(createReqVO.getType(), IotProductFunctionTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } return function.getId(); } + private void validateProductStatus(Long createReqVO) { + IotProductDO product = productService.getProduct(createReqVO); + if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getType())) { + throw exception(PRODUCT_STATUS_NOT_ALLOW_FUNCTION); + } + } + private void validateNotDefaultEventAndService(String identifier) { // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { @@ -109,11 +120,14 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe // 2. 校验功能标识符是否唯一 validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); - // 3. 更新数据库 + // 3. 校验产品状态,发布状态下,不允许操作功能 + validateProductStatus(updateReqVO.getProductId()); + + // 4. 更新数据库 IotThinkModelFunctionDO thinkModelFunction = IotThinkModelFunctionConvert.INSTANCE.convert(updateReqVO); thinkModelFunctionMapper.updateById(thinkModelFunction); - // 4. 如果更新的是属性,需要更新默认的事件和服务 + // 5. 如果更新的是属性,需要更新默认的事件和服务 if (Objects.equals(updateReqVO.getType(), IotProductFunctionTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } @@ -135,6 +149,9 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } + // 3. 校验产品状态,发布状态下,不允许操作功能 + validateProductStatus(functionDO.getProductId()); + // 2. 删除功能 thinkModelFunctionMapper.deleteById(id); From 8c84ac9d8a88dd73d03746274f4f6e2de7004668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 27 Oct 2024 21:26:35 +0800 Subject: [PATCH 006/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20mqtt=20=E6=95=B0=E6=8D=AE=E6=8E=A5?= =?UTF-8?q?=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 5 ++ .../tdengine/ThingModelMessage.java | 70 +++++++++++++++++++ .../iot/emq/service/EmqxServiceImpl.java | 14 +++- .../service/device/IotDeviceDataService.java | 26 +++++++ .../device/IotDeviceDataServiceImpl.java | 39 +++++++++++ .../iot/service/device/IotDeviceService.java | 9 +++ ...iceImpl.java => IotDeviceServiceImpl.java} | 9 ++- .../IotDbStructureDataServiceImpl.java | 16 ++--- ...neService.java => IotTdEngineService.java} | 10 +-- ...eImpl.java => IotTdEngineServiceImpl.java} | 2 +- .../tdengine/IotThingModelMessageService.java | 16 +++++ .../IotThingModelMessageServiceImpl.java | 13 ++++ .../mapper/tdengine/TdEngineMapper.xml | 18 +++-- 13 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{DeviceServiceImpl.java => IotDeviceServiceImpl.java} (95%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{TdEngineService.java => IotTdEngineService.java} (91%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{TdEngineServiceImpl.java => IotTdEngineServiceImpl.java} (97%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index cb2fd818f9..013287d2b9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -25,6 +25,11 @@ ${revision} + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + cn.iocoder.boot diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java new file mode 100644 index 0000000000..d5009dc244 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +/** + * 物模型消息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ThingModelMessage { + + /** + * 消息ID + */ + private String id; + + /** + * 扩展功能的参数 + */ + private Object sys; + + /** + * 请求方法 例如:thing.event.property.post + */ + private String method; + + /** + * 请求参数 + */ + private Object params; + + /** + * 属性上报时间戳 + */ + private Long time; + + /** + * 设备信息 + */ + private String productKey; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 设备 key + */ + private String deviceKey; + + /** + * 转换为 Map 类型 + */ + public Map dataToMap() { + Map mapData = new HashMap<>(); + if (params instanceof Map) { + ((Map) params).forEach((key, value) -> mapData.put(key.toString(), value)); + } + return mapData; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 0c1a87f7ff..c82d0d3057 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.iot.emq.service; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttMessage; @@ -16,13 +19,20 @@ import org.springframework.stereotype.Service; @Service public class EmqxServiceImpl implements EmqxService { + @Resource + private IotDeviceDataService iotDeviceDataService; + // TODO 多线程处理消息 @Override public void subscribeCallback(String topic, MqttMessage mqttMessage) { log.info("收到消息,主题: {}, 内容: {}", topic, new String(mqttMessage.getPayload())); // 根据不同的主题,处理不同的业务逻辑 if (topic.contains("/property/post")) { - // 设备上报数据 + // 设备上报数据 topic /sys/f13f57c63e9/dianbiao1/thing/event/property/post + String productKey = topic.split("/")[2]; + String deviceName = topic.split("/")[3]; + String message = new String(mqttMessage.getPayload()); + iotDeviceDataService.saveDeviceData(productKey, deviceName, message); } } @@ -30,7 +40,7 @@ public class EmqxServiceImpl implements EmqxService { public void subscribe(MqttClient client) { try { // 订阅默认主题,可以根据需要修改 -// client.subscribe("$share/yudao/+/+/#", 1); + client.subscribe("/sys/+/+/#", 1); log.info("订阅默认主题成功"); } catch (Exception e) { log.error("订阅默认主题失败", e); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java new file mode 100644 index 0000000000..4f3ef37d6c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import jakarta.validation.Valid; + +/** + * IoT 设备数据 Service 接口 + * + * @author 芋道源码 + */ +public interface IotDeviceDataService { + + + /** + * 保存设备数据 + * + * @param productKey 产品 key + * @param deviceName 设备名称 + * @param message 消息 + */ + void saveDeviceData(String productKey, String deviceName, String message); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java new file mode 100644 index 0000000000..8f0c859ad6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.hutool.json.JSONObject; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class IotDeviceDataServiceImpl implements IotDeviceDataService { + + @Resource + private IotDeviceService deviceService; + @Resource + private IotThingModelMessageService thingModelMessageService; + + @Override + public void saveDeviceData(String productKey, String deviceName, String message) { + // 1. 根据产品 key 和设备名称,获得设备信息 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName); + // 2. 解析消息,保存数据 + JSONObject jsonObject = new JSONObject(message); + log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject); + ThingModelMessage thingModelMessage = ThingModelMessage.builder() + .id(jsonObject.getStr("id")) + .sys(jsonObject.get("sys")) + .method(jsonObject.getStr("method")) + .params(jsonObject.get("params")) + .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) + .productKey(productKey) + .deviceName(deviceName) + .deviceKey(device.getDeviceKey()) + .build(); + thingModelMessageService.saveThingModelMessage(thingModelMessage); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 032a7478ee..c967a957ed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -64,4 +64,13 @@ public interface IotDeviceService { * @return 设备数量 */ Long getDeviceCountByProductId(Long productId); + + /** + * 根据产品 key 和设备名称,获得设备信息 + * + * @param productKey 产品 key + * @param deviceName 设备名称 + * @return 设备信息 + */ + IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java similarity index 95% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 2ae08bb94b..d24975b72e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; @@ -32,7 +33,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j -public class DeviceServiceImpl implements IotDeviceService { +public class IotDeviceServiceImpl implements IotDeviceService { @Resource private IotDeviceMapper deviceMapper; @@ -224,4 +225,10 @@ public class DeviceServiceImpl implements IotDeviceService { return deviceMapper.selectCountByProductId(productId); } + @Override + @TenantIgnore + public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) { + return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java index d42fd72fc9..0506546be5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java @@ -25,7 +25,7 @@ import java.util.stream.Collectors; public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { @Resource - private TdEngineService tdEngineService; + private IotTdEngineService iotTdEngineService; @Resource private TdRestApi tdRestApi; @@ -87,7 +87,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService // 5. 创建超级表 String dataBaseName = url.substring(url.lastIndexOf("/") + 1); - tdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); + iotTdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); } @Override @@ -107,7 +107,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService private List getTableFields(String tableName) { List fields = new ArrayList<>(); // 获取超级表的描述信息 - List> maps = tdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName); + List> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName); if (maps != null) { // 过滤掉 note 字段为 TAG 的记录 maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList(); @@ -133,16 +133,16 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService String dataBaseName = url.substring(url.lastIndexOf("/") + 1); // 添加新增字段 if (CollUtil.isNotEmpty(addFields)) { - tdEngineService.addColumnForSuperTable(dataBaseName,tableName, addFields); + iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, addFields); } // 删除旧字段 if (CollUtil.isNotEmpty(dropFields)) { - tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, dropFields); + iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, dropFields); } // 修改字段(先删除再添加) if (CollUtil.isNotEmpty(modifyFields)) { - tdEngineService.dropColumnForSuperTable(dataBaseName,tableName, modifyFields); - tdEngineService.addColumnForSuperTable(dataBaseName,tableName, modifyFields); + iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, modifyFields); + iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, modifyFields); } } @@ -181,7 +181,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); String dataBaseName = url.substring(url.lastIndexOf("/") + 1); - Integer tableExists = tdEngineService.checkSuperTableExists(dataBaseName, superTableName); + Integer tableExists = iotTdEngineService.checkSuperTableExists(dataBaseName, superTableName); if (tableExists != null && tableExists > 0) { updateSuperTable(thingModel, product.getDeviceType()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java similarity index 91% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java index 9319adbd77..3175d28399 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java @@ -12,7 +12,7 @@ import java.util.Map; /** * TdEngineService */ -public interface TdEngineService { +public interface IotTdEngineService { /** * 创建数据库 @@ -45,20 +45,20 @@ public interface TdEngineService { /** * 为超级表添加列 * - * @param dataBaseName 数据库名称 + * @param dataBaseName 数据库名称 * @param superTableName 超级表名称 * @param fieldsVo 字段信息 */ - void addColumnForSuperTable(String dataBaseName,String superTableName, List fieldsVo); + void addColumnForSuperTable(String dataBaseName, String superTableName, List fieldsVo); /** * 为超级表删除列 * - * @param dataBaseName 数据库名称 + * @param dataBaseName 数据库名称 * @param superTableName 超级表名称 * @param fieldsVo 字段信息 */ - void dropColumnForSuperTable(String dataBaseName,String superTableName, List fieldsVo); + void dropColumnForSuperTable(String dataBaseName, String superTableName, List fieldsVo); /** * 为超级表添加tag diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java similarity index 97% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java index d61b789222..3c87f624db 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java @@ -15,7 +15,7 @@ import java.util.Map; @Service @Slf4j -public class TdEngineServiceImpl implements TdEngineService { +public class IotTdEngineServiceImpl implements IotTdEngineService { @Resource private TdEngineMapper tdEngineMapper; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java new file mode 100644 index 0000000000..937338544d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; + +/** + * 物模型消息 Service + */ +public interface IotThingModelMessageService { + + /** + * 保存物模型消息 + * + * @param thingModelMessage 物模型消息 + */ + void saveThingModelMessage(ThingModelMessage thingModelMessage); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java new file mode 100644 index 0000000000..d477564238 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import org.springframework.stereotype.Service; + +@Service +public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { + + @Override + public void saveThingModelMessage(ThingModelMessage thingModelMessage) { + // TODO 芋艿,后续实现 + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml index f1d7481981..d3f1088a1f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -2,9 +2,10 @@ + - create database if not exists #{dataBaseName} + CREATE DATABASE IF NOT EXISTS ${dataBaseName} @@ -271,7 +272,8 @@ select last(*) - from #{dataBaseName}.#{stableName} group by ${tagsName} + from #{dataBaseName}.#{stableName} + group by ${tagsName} From 3dafd31da6ba6dad4beba2954ca5ed689a209699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Thu, 31 Oct 2024 21:47:54 +0800 Subject: [PATCH 007/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E6=95=B0=E6=8D=AE=E6=8E=A5=E6=94=B6?= =?UTF-8?q?=EF=BC=8CJSON=20=E6=A0=87=E5=87=86=E6=A0=BC=E5=BC=8F=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=8E=A5=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/dal/dataobject/tdengine/TableDO.java | 27 ++++ .../dal/dataobject/tdengine/TableData.java | 17 ++- .../dal/dataobject/tdengine/TdFieldDO.java | 16 +++ .../IotThinkModelFunctionMapper.java | 3 + .../iot/dal/tdengine/TdEngineMapper.java | 17 ++- .../iot/emq/service/EmqxServiceImpl.java | 2 + .../device/IotDeviceDataServiceImpl.java | 2 +- .../service/device/IotDeviceServiceImpl.java | 23 +++- .../IotDbStructureDataServiceImpl.java | 19 +-- .../service/tdengine/IotTdEngineService.java | 8 +- .../tdengine/IotTdEngineServiceImpl.java | 6 +- .../tdengine/IotThingModelMessageService.java | 4 +- .../IotThingModelMessageServiceImpl.java | 118 +++++++++++++++++- .../IotThinkModelFunctionService.java | 8 ++ .../IotThinkModelFunctionServiceImpl.java | 5 + .../mapper/tdengine/TdEngineMapper.xml | 27 ++-- 16 files changed, 247 insertions(+), 55 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java new file mode 100644 index 0000000000..11751ff643 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import cn.iocoder.yudao.module.iot.domain.BaseEntity; +import lombok.Data; + +import java.util.List; + +@Data +public class TableDO extends BaseEntity { + + /** + * 超级表普通列字段的值 + * 值需要与创建超级表时普通列字段的数据类型对应上 + */ + private List schemaFieldValues; + + /** + * 超级表标签字段的值 + * 值需要与创建超级表时标签字段的数据类型对应上 + */ + private List tagsFieldValues; + + /** + * 表名称 + */ + private String tableName; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java index e9acbb7f0a..dfb3ed2129 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java @@ -15,18 +15,23 @@ public class TableData { */ private List schemaFieldList; - /** - * 超级表标签字段的值 - * 值需要与创建超级表时标签字段的数据类型对应上 - */ - private List tagsValueList; - /** * 超级表普通列字段的值 * 值需要与创建超级表时普通列字段的数据类型对应上 */ private List schemaValueList; + /** + * 超级表标签字段的名称 + */ + private List tagsFieldList; + + /** + * 超级表标签字段的值 + * 值需要与创建超级表时标签字段的数据类型对应上 + */ + private List tagsValueList; + /** * 表名称 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index d9cf28f1bb..7656527489 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java @@ -19,6 +19,11 @@ public class TdFieldDO { */ private String fieldName; + /** + * 字段值 + */ + private Object fieldValue; + /** * 字段类型 */ @@ -28,4 +33,15 @@ public class TdFieldDO { * 字段长度 */ private Integer dataLength = 0; + + public TdFieldDO(String fieldName, String dataType, Integer dataLength) { + this.fieldName = fieldName; + this.dataType = dataType; + this.dataLength = dataLength; + } + + public TdFieldDO(String fieldName, Object fieldValue) { + this.fieldName = fieldName; + this.fieldValue = fieldValue; + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java index e8b96e022d..2883abe4e3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java @@ -55,4 +55,7 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX selectListByProductKey(String productKey){ + return selectList(IotThinkModelFunctionDO::getProductKey, productKey); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java index 85e324a77c..94db527c28 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.domain.FieldsVo; import cn.iocoder.yudao.module.iot.domain.SelectDto; -import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; import com.baomidou.dynamic.datasource.annotation.DS; @@ -86,9 +86,20 @@ public interface TdEngineMapper { @Param("superTableName") String superTableName, @Param("field") TdFieldDO field); - void createTable(TableDto tableDto); + /** + * 创建表 - 创建超级表的子表 + * @param tableDO 表信息 + */ + @InterceptorIgnore(tenantLine = "true") + void createTable(TableDO tableDO); - void insertData(TableDto tableDto); + /** + * 插入数据 - 指定列插入数据 + * + * @param tableDto 数据 + */ + @InterceptorIgnore(tenantLine = "true") + void insertData(TableDO tableDto); List> selectByTimestamp(SelectDto selectDto); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index c82d0d3057..f338d62498 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -6,6 +6,7 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttMessage; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; // TODO @芋艿:在瞅瞅 @@ -24,6 +25,7 @@ public class EmqxServiceImpl implements EmqxService { // TODO 多线程处理消息 @Override + @Async public void subscribeCallback(String topic, MqttMessage mqttMessage) { log.info("收到消息,主题: {}, 内容: {}", topic, new String(mqttMessage.getPayload())); // 根据不同的主题,处理不同的业务逻辑 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 8f0c859ad6..6b7aac21f9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -34,6 +34,6 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { .deviceName(deviceName) .deviceKey(device.getDeviceKey()) .build(); - thingModelMessageService.saveThingModelMessage(thingModelMessage); + thingModelMessageService.saveThingModelMessage(device,thingModelMessage); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index d24975b72e..f3243900e7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -20,6 +20,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; +import java.util.Objects; import java.util.UUID; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -213,10 +214,30 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Override public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { // 校验存在 - validateDeviceExists(updateReqVO.getId()); + IotDeviceDO iotDeviceDO = validateDeviceExists(updateReqVO.getId()); // 更新状态和更新时间 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); + + // 以前是未激活,现在是上线,设置设备激活时间 + if (Objects.equals(iotDeviceDO.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus()) + && Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { + updateObj.setActiveTime(LocalDateTime.now()); + } + + // 如果是上线,设置上线时间 + if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { + updateObj.setLastOnlineTime(LocalDateTime.now()); + } + + // 如果是离线,设置离线时间 + if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) { + updateObj.setLastOfflineTime(LocalDateTime.now()); + } + + // 设置状态更新时间 + updateObj.setStatusLastUpdateTime(LocalDateTime.now()); + deviceMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java index 0506546be5..b7de8882a9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java @@ -60,27 +60,10 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService dataType("NCHAR"). dataLength(64). build()); - // 年 tagsFields.add(TdFieldDO.builder(). - fieldName("year"). + fieldName("device_type"). dataType("INT"). build()); - // 月 - tagsFields.add(TdFieldDO.builder(). - fieldName("month"). - dataType("INT"). - build()); - // 日 - tagsFields.add(TdFieldDO.builder(). - fieldName("day"). - dataType("INT"). - build()); - // 时 - tagsFields.add(TdFieldDO.builder(). - fieldName("hour"). - dataType("INT"). - build()); - // 4. 获取超级表的名称 String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java index 3175d28399..753284acae 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.iot.service.tdengine; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.domain.SelectDto; -import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; @@ -70,17 +70,15 @@ public interface IotTdEngineService { * 创建表 * * @param tableDto 表信息 - * @throws Exception 异常 */ - void createTable(TableDto tableDto) throws Exception; + void createTable(TableDO tableDto); /** * 插入数据 * * @param tableDto 表信息 - * @throws Exception 异常 */ - void insertData(TableDto tableDto) throws Exception; + void insertData(TableDO tableDto); /** * 根据时间戳查询数据 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java index 3c87f624db..c7f7e73093 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.service.tdengine; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; import cn.iocoder.yudao.module.iot.domain.SelectDto; -import cn.iocoder.yudao.module.iot.domain.TableDto; import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; import jakarta.annotation.Resource; @@ -31,12 +31,12 @@ public class IotTdEngineServiceImpl implements IotTdEngineService { } @Override - public void createTable(TableDto tableDto) { + public void createTable(TableDO tableDto) { tdEngineMapper.createTable(tableDto); } @Override - public void insertData(TableDto tableDto) { + public void insertData(TableDO tableDto) { tdEngineMapper.insertData(tableDto); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java index 937338544d..b2f51a7f83 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.tdengine; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; /** @@ -10,7 +11,8 @@ public interface IotThingModelMessageService { /** * 保存物模型消息 * + * @param device 设备 * @param thingModelMessage 物模型消息 */ - void saveThingModelMessage(ThingModelMessage thingModelMessage); + void saveThingModelMessage(IotDeviceDO device,ThingModelMessage thingModelMessage); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index d477564238..ae7fa51ee7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -1,13 +1,127 @@ package cn.iocoder.yudao.module.iot.service.tdengine; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; +import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j @Service public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") + private String url; + + @Resource + private IotThinkModelFunctionService iotThinkModelFunctionService; + @Resource + private IotDeviceService iotDeviceService; + @Resource + private IotTdEngineService iotTdEngineService; + @Override - public void saveThingModelMessage(ThingModelMessage thingModelMessage) { - // TODO 芋艿,后续实现 + @TenantIgnore + public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { + // 判断设备状态,如果为未激活状态,创建数据表 + if (device.getStatus().equals(0)) { + // 创建设备数据表 + createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); + // 更新设备状态 + IotDeviceStatusUpdateReqVO updateReqVO = new IotDeviceStatusUpdateReqVO(); + updateReqVO.setId(device.getId()); + updateReqVO.setStatus(IotDeviceStatusEnum.ONLINE.getStatus()); + iotDeviceService.updateDeviceStatus(updateReqVO); + } + + // 1. 获取设备属性 + Map params = thingModelMessage.dataToMap(); + + // 2. 物模型校验,过滤非物模型属性 + List thinkModelFunctionListByProductKey = iotThinkModelFunctionService.getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey()); + + // 2.1 筛选是属性 IotProductFunctionTypeEnum + thinkModelFunctionListByProductKey.removeIf(iotThinkModelFunctionDO -> !iotThinkModelFunctionDO.getType().equals(IotProductFunctionTypeEnum.PROPERTY.getType())); + if (thinkModelFunctionListByProductKey.isEmpty()) { + return; + } + // 2.2 获取属性名称 + Map thingModelProperties = thinkModelFunctionListByProductKey.stream().collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, IotThinkModelFunctionDO::getName)); + + // 4. 保存属性记录 + List schemaFieldValues = new ArrayList<>(); + + // 1. 设置字段名 + schemaFieldValues.add(new TdFieldDO("time", thingModelMessage.getTime())); + + // 2. 遍历新属性 + params.forEach((key, val) -> { + if (thingModelProperties.containsKey(key)) { + schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); + } + }); + + // 3. 保存设备属性 + TableDO tableData = new TableDO(); + tableData.setDataBaseName(url.substring(url.lastIndexOf("/") + 1)); + tableData.setSuperTableName(getProductPropertySTableName(device.getDeviceType(), device.getProductKey())); + tableData.setTableName("device_" + device.getProductKey().toLowerCase() + "_" + device.getDeviceName().toLowerCase()); + tableData.setSchemaFieldValues(schemaFieldValues); + + // 4. 保存数据 + iotTdEngineService.insertData(tableData); + } + + private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) { + List tagsFieldValues = new ArrayList<>(); + String SuperTableName = getProductPropertySTableName(deviceType, productKey); + List> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), SuperTableName); + if (maps != null) { + List> taggedNotesList = maps.stream().filter(map -> "TAG".equals(map.get("note"))).toList(); + tagsFieldValues = FieldParser.parse(taggedNotesList.stream() + .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) + .collect(Collectors.toList())); + for (TdFieldDO tagsFieldValue : tagsFieldValues) { + switch (tagsFieldValue.getFieldName()) { + case "product_key" -> tagsFieldValue.setFieldValue(productKey); + case "device_key" -> tagsFieldValue.setFieldValue(deviceKey); + case "device_name" -> tagsFieldValue.setFieldValue(deviceName); + case "device_type" -> tagsFieldValue.setFieldValue(deviceType); + } + } + } + + // 1. 创建设备数据表 + String tableName = "device_" + productKey.toLowerCase() + "_" + deviceName.toLowerCase(); + TableDO tableDto = new TableDO(); + tableDto.setDataBaseName(url.substring(url.lastIndexOf("/") + 1)); + tableDto.setSuperTableName(SuperTableName); + tableDto.setTableName(tableName); + tableDto.setTagsFieldValues(tagsFieldValues); + iotTdEngineService.createTable(tableDto); + } + + static String getProductPropertySTableName(Integer deviceType, String productKey) { + return switch (deviceType) { + case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); + case 2 -> String.format("gateway_%s", productKey).toLowerCase(); + default -> String.format("device_%s", productKey).toLowerCase(); + }; } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java index 3067772e66..a6bf7b4583 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java @@ -68,4 +68,12 @@ public interface IotThinkModelFunctionService { * @param productId 产品编号 */ void createSuperTableDataModel(Long productId); + + /** + * 获得产品物模型列表 + * + * @param productKey 产品 Key + * @return 产品物模型列表 + */ + List getThinkModelFunctionListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index dd7a51786b..25cfe7b462 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -199,6 +199,11 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe dbStructureDataService.createSuperTableDataModel(product, functionList); } + @Override + public List getThinkModelFunctionListByProductKey(String productKey) { + return thinkModelFunctionMapper.selectListByProductKey(productKey); + } + /** * 创建默认的事件和服务 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml index d3f1088a1f..9d5cf2a43e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -110,30 +110,27 @@ + - create table - if not exists #{dataBaseName}.#{tableName} - using #{dataBaseName}.#{superTableName} - tags - + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName} + + ${item.fieldName} + + TAGS + #{item.fieldValue} + - insert into #{dataBaseName}.#{tableName} + INSERT INTO ${dataBaseName}.${tableName} - #{item.fieldName} + ${item.fieldName} - using #{dataBaseName}.#{superTableName} - tags - - #{item.fieldValue} - - values + VALUES #{item.fieldValue} From 624f5283b3ec89295f3562a068bd009881b1c487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 3 Nov 2024 00:16:46 +0800 Subject: [PATCH 008/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E8=AE=BE=E5=A4=87=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E5=92=8C=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/IotProductFunctionTypeEnum.java | 2 +- .../admin/device/IotDeviceController.java | 8 +- .../admin/device/IotDeviceDataController.java | 38 +++++ .../vo/{ => device}/IotDevicePageReqVO.java | 2 +- .../vo/{ => device}/IotDeviceRespVO.java | 2 +- .../vo/{ => device}/IotDeviceSaveReqVO.java | 2 +- .../IotDeviceStatusUpdateReqVO.java | 2 +- .../vo/deviceData/IotDeviceDataReqVO.java | 21 +++ .../vo/deviceData/IotDeviceDataRespVO.java | 42 ++++++ .../convert/device/IotDeviceDataConvert.java | 46 ++++++ .../dataobject/device/IotDeviceDataDO.java | 71 +++++++++ .../iot/dal/mysql/device/IotDeviceMapper.java | 2 +- .../iot/dal/redis/RedisKeyConstants.java | 20 +++ .../redis/deviceData/DeviceDataRedisDAO.java | 43 ++++++ .../service/device/IotDeviceDataService.java | 17 ++- .../device/IotDeviceDataServiceImpl.java | 57 ++++++- .../iot/service/device/IotDeviceService.java | 4 +- .../service/device/IotDeviceServiceImpl.java | 6 +- .../IotDbStructureDataServiceImpl.java | 106 ++++++------- .../IotThingModelMessageServiceImpl.java | 140 ++++++++++++++---- 20 files changed, 523 insertions(+), 108 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/{ => device}/IotDevicePageReqVO.java (97%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/{ => device}/IotDeviceRespVO.java (97%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/{ => device}/IotDeviceSaveReqVO.java (89%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/{ => device}/IotDeviceStatusUpdateReqVO.java (91%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java index b99d2b0938..c8a03a4d39 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java @@ -30,7 +30,7 @@ public enum IotProductFunctionTypeEnum implements IntArrayValuable { */ private final String description; - public static IotProductFunctionTypeEnum valueOf(Integer type) { + public static IotProductFunctionTypeEnum valueOfType(Integer type) { for (IotProductFunctionTypeEnum value : values()) { if (value.getType().equals(type)) { return value; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 6d75f1cdd2..ba68e02328 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import io.swagger.v3.oas.annotations.Operation; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java new file mode 100644 index 0000000000..93b368bd8d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 设备数据") +@RestController +@RequestMapping("/iot/device/data") +@Validated +public class IotDeviceDataController { + + @Resource + private IotDeviceDataService deviceDataService; + + @GetMapping("/latest-data") + @Operation(summary = "获取设备属性最新数据") + public CommonResult> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) { + List list = deviceDataService.getDevicePropertiesLatestData(deviceDataReqVO); + return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java similarity index 97% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java index 1c29f9f810..90ded80898 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo; +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java similarity index 97% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java index 0b2cf25f56..fd2fbaa686 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo; +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java similarity index 89% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index 620e5310fa..85fde9d3d9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo; +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceStatusUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceStatusUpdateReqVO.java similarity index 91% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceStatusUpdateReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceStatusUpdateReqVO.java index a91a586906..daa1efde80 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceStatusUpdateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceStatusUpdateReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo; +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java new file mode 100644 index 0000000000..b8a95f607e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - IoT 设备数据 Request VO") +@Data +public class IotDeviceDataReqVO { + + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + private Long deviceId; + + @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) + private String identifier; + + @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java new file mode 100644 index 0000000000..256bf84faa --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - IoT 设备数据 Response VO") +@Data +public class IotDeviceDataRespVO { + + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + private Long deviceId; + + @Schema(description = "物模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") + private Long thinkModelFunctionId; + + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) + private String productKey; + + @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String deviceName; + + @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) + private String identifier; + + @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + + @Schema(description = "数据类型", requiredMode = Schema.RequiredMode.REQUIRED) + private String dataType; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + + @Schema(description = "最新值", requiredMode = Schema.RequiredMode.REQUIRED) + private String value; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java new file mode 100644 index 0000000000..3bad54ba09 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.convert.device; + +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Mapper +public interface IotDeviceDataConvert { + + IotDeviceDataConvert INSTANCE = Mappers.getMapper(IotDeviceDataConvert.class); + +// default List convert(Map deviceData, IotDeviceDO device){ +// List list = new ArrayList<>(); +// deviceData.forEach((identifier, value) -> { +//// ThingModelProperty property = ThingModelService.INSTANCE.getProperty(device.getProductId(), identifier); +//// if (Objects.isNull(property)) { +//// return; +//// } +// IotDeviceDataRespVO vo = new IotDeviceDataRespVO(); +// vo.setDeviceId(device.getId()); +// vo.setProductKey(device.getProductKey()); +// vo.setDeviceName(device.getDeviceName()); +// vo.setIdentifier(identifier); +//// vo.setName(property.getName()); +//// vo.setDataType(property.getDataType().getType()); +// vo.setUpdateTime(device.getUpdateTime()); +// vo.setValue(value.toString()); +// list.add(vo); +// }); +// return list; +// } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java new file mode 100644 index 0000000000..5d80511721 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.device; + +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; +import com.baomidou.mybatisplus.annotation.TableId; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * IoT 设备数据 DO + * + * @author haohao + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotDeviceDataDO { + + /** + * 设备编号 + */ + private Long deviceId; + + /** + * 物模型编号 + */ + private Long thinkModelFunctionId; + + /** + * 产品标识 + */ + private String productKey; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 属性标识符 + */ + private String identifier; + + /** + * 属性名称 + */ + private String name; + + /** + * 数据类型 + */ + private String dataType; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + + /** + * 最新值 + */ + private String value; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 0e5552f83b..3129f400c2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import org.apache.ibatis.annotations.Mapper; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000000..25ea7a2926 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.dal.redis; + + + +/** + * Iot Redis Key 枚举类 + * + * @author 芋道源码 + */ +public interface RedisKeyConstants { + + /** + * 设备属性数据缓存 + *

+ * KEY 格式:device_property_data:{deviceId} + * VALUE 数据类型:String 设备属性数据 + */ + String DEVICE_PROPERTY_DATA = "device_property_data:%s_%s_%s"; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java new file mode 100644 index 0000000000..b1b69f8dce --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.iot.dal.redis.deviceData; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY_DATA; + +/** + * {@link IotDeviceDataDO} 的 Redis DAO + */ +@Repository +public class DeviceDataRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + public IotDeviceDataDO get(String productKey, String deviceName, String identifier) { + String redisKey = formatKey(productKey, deviceName, identifier); + return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), IotDeviceDataDO.class); + } + + public void set(IotDeviceDataDO deviceData) { + String redisKey = formatKey(deviceData.getProductKey(), deviceData.getDeviceName(), deviceData.getIdentifier()); + stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(deviceData)); + } + + public void delete(String productKey, String deviceName, String identifier) { + String redisKey = formatKey(productKey, deviceName, identifier); + stringRedisTemplate.delete(redisKey); + } + + private static String formatKey(String productKey, String deviceName, String identifier) { + return String.format(DEVICE_PROPERTY_DATA, productKey, deviceName, identifier); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java index 4f3ef37d6c..f67f426855 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -1,12 +1,11 @@ package cn.iocoder.yudao.module.iot.service.device; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import jakarta.validation.Valid; +import java.util.List; + /** * IoT 设备数据 Service 接口 * @@ -23,4 +22,12 @@ public interface IotDeviceDataService { * @param message 消息 */ void saveDeviceData(String productKey, String deviceName, String message); + + /** + * 获得设备属性最新数据 + * + * @param deviceId 设备编号 + * @return 设备属性最新数据 + */ + List getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 6b7aac21f9..86ac630c6f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -1,13 +1,23 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.json.JSONObject; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; +import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import jakarta.annotation.Resource; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.List; + @Slf4j @Service public class IotDeviceDataServiceImpl implements IotDeviceDataService { @@ -16,6 +26,11 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { private IotDeviceService deviceService; @Resource private IotThingModelMessageService thingModelMessageService; + @Resource + private IotThinkModelFunctionService thinkModelFunctionService; + + @Resource + private DeviceDataRedisDAO deviceDataRedisDAO; @Override public void saveDeviceData(String productKey, String deviceName, String message) { @@ -34,6 +49,46 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { .deviceName(deviceName) .deviceKey(device.getDeviceKey()) .build(); - thingModelMessageService.saveThingModelMessage(device,thingModelMessage); + thingModelMessageService.saveThingModelMessage(device, thingModelMessage); + } + + @Override + public List getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) { + List list = new ArrayList<>(); + // 1. 获取设备信息 + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); + // 2. 获取设备属性最新数据 + List thinkModelFunctionList = thinkModelFunctionService.getThinkModelFunctionListByProductKey(device.getProductKey()); + thinkModelFunctionList = thinkModelFunctionList.stream() + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType() + .equals(function.getType())).toList(); + + // 3. 过滤标识符和属性名称 + if (deviceDataReqVO.getIdentifier() != null) { + thinkModelFunctionList = thinkModelFunctionList.stream() + .filter(function -> function.getIdentifier().toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())) + .toList(); + } + if (deviceDataReqVO.getName() != null) { + thinkModelFunctionList = thinkModelFunctionList.stream() + .filter(function -> function.getName().toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())) + .toList(); + } + // 4. 获取设备属性最新数据 + thinkModelFunctionList.forEach(function -> { + IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), function.getIdentifier()); + if (deviceData == null) { + deviceData = new IotDeviceDataDO(); + deviceData.setProductKey(device.getProductKey()); + deviceData.setDeviceName(device.getDeviceName()); + deviceData.setIdentifier(function.getIdentifier()); + deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); + deviceData.setThinkModelFunctionId(function.getId()); + deviceData.setName(function.getName()); + deviceData.setDataType(function.getProperty().getDataType().getType()); + } + list.add(deviceData); + }); + return list; } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index c967a957ed..ed4c6a60ce 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import jakarta.validation.*; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.framework.common.pojo.PageResult; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index f3243900e7..60743f6713 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -5,9 +5,9 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java index b7de8882a9..9471781027 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.iot.service.tdengine; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdRestApi; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import jakarta.annotation.Resource; @@ -14,10 +14,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; @Service @@ -27,9 +24,6 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService @Resource private IotTdEngineService iotTdEngineService; - @Resource - private TdRestApi tdRestApi; - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; @@ -37,39 +31,25 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { // 1. 解析物模型,获得字段列表 List schemaFields = new ArrayList<>(); - schemaFields.add(TdFieldDO.builder(). - fieldName("time"). - dataType("TIMESTAMP"). - build()); + schemaFields.add(TdFieldDO.builder() + .fieldName("time") + .dataType("TIMESTAMP") + .build()); schemaFields.addAll(FieldParser.parse(thingModel)); // 3. 设置超级表的标签 - List tagsFields = new ArrayList<>(); - tagsFields.add(TdFieldDO.builder(). - fieldName("product_key"). - dataType("NCHAR"). - dataLength(64). - build()); - tagsFields.add(TdFieldDO.builder(). - fieldName("device_key"). - dataType("NCHAR"). - dataLength(64). - build()); - tagsFields.add(TdFieldDO.builder(). - fieldName("device_name"). - dataType("NCHAR"). - dataLength(64). - build()); - tagsFields.add(TdFieldDO.builder(). - fieldName("device_type"). - dataType("INT"). - build()); + List tagsFields = Arrays.asList( + TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("device_type").dataType("INT").build() + ); // 4. 获取超级表的名称 String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); // 5. 创建超级表 - String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + String dataBaseName = getDatabaseName(); iotTdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); } @@ -81,7 +61,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService List newFields = FieldParser.parse(thingModel); updateTableFields(tbName, oldFields, newFields); - } catch (Throwable e) { + } catch (Exception e) { log.error("更新物模型超级表失败", e); } } @@ -90,14 +70,15 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService private List getTableFields(String tableName) { List fields = new ArrayList<>(); // 获取超级表的描述信息 - List> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), tableName); + List> maps = iotTdEngineService.describeSuperTable(getDatabaseName(), tableName); if (maps != null) { - // 过滤掉 note 字段为 TAG 的记录 - maps = maps.stream().filter(map -> !"TAG".equals(map.get("note"))).toList(); - // 过滤掉 time 字段 - maps = maps.stream().filter(map -> !"time".equals(map.get("field"))).toList(); + // 过滤掉 note 字段为 TAG 的记录和 time 字段 + List> filteredMaps = maps.stream() + .filter(map -> !"TAG".equals(map.get("note"))) + .filter(map -> !"time".equals(map.get("field"))) + .toList(); // 解析字段信息 - fields = FieldParser.parse(maps.stream() + fields = FieldParser.parse(filteredMaps.stream() .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) .collect(Collectors.toList())); } @@ -113,7 +94,7 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService // 获取删除字段 List dropFields = getDropFields(oldFields, newFields); - String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + String dataBaseName = getDatabaseName(); // 添加新增字段 if (CollUtil.isNotEmpty(addFields)) { iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, addFields); @@ -131,25 +112,37 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService // 获取新增字段 private List getAddFields(List oldFields, List newFields) { + Set oldFieldNames = oldFields.stream() + .map(TdFieldDO::getFieldName) + .collect(Collectors.toSet()); return newFields.stream() - .filter(f -> oldFields.stream().noneMatch(old -> old.getFieldName().equals(f.getFieldName()))) + .filter(f -> !oldFieldNames.contains(f.getFieldName())) .collect(Collectors.toList()); } // 获取修改字段 private List getModifyFields(List oldFields, List newFields) { + Map oldFieldMap = oldFields.stream() + .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); + return newFields.stream() - .filter(f -> oldFields.stream().anyMatch(old -> - old.getFieldName().equals(f.getFieldName()) && - (!old.getDataType().equals(f.getDataType()) || !Objects.equals(old.getDataLength(), f.getDataLength())))) + .filter(f -> { + TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); + return oldField != null && + (!oldField.getDataType().equals(f.getDataType()) || + !Objects.equals(oldField.getDataLength(), f.getDataLength())); + }) .collect(Collectors.toList()); } // 获取删除字段 private List getDropFields(List oldFields, List newFields) { + Set newFieldNames = newFields.stream() + .map(TdFieldDO::getFieldName) + .collect(Collectors.toSet()); return oldFields.stream() - .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName()) && - newFields.stream().noneMatch(n -> n.getFieldName().equals(f.getFieldName()))) + .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName())) + .filter(f -> !newFieldNames.contains(f.getFieldName())) .collect(Collectors.toList()); } @@ -157,13 +150,13 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService public void createSuperTableDataModel(IotProductDO product, List functionList) { ThingModelRespVO thingModel = buildThingModel(product, functionList); - if (thingModel.getModel().getProperties().isEmpty()) { + if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { log.warn("物模型属性列表为空,不创建超级表"); return; } String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); - String dataBaseName = url.substring(url.lastIndexOf("/") + 1); + String dataBaseName = getDatabaseName(); Integer tableExists = iotTdEngineService.checkSuperTableExists(dataBaseName, superTableName); if (tableExists != null && tableExists > 0) { @@ -180,7 +173,8 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService ThingModelRespVO.Model model = new ThingModelRespVO.Model(); List properties = functionList.stream() - .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals(IotProductFunctionTypeEnum.valueOf(function.getType()))) + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals( + IotProductFunctionTypeEnum.valueOfType(function.getType()))) .map(this::buildThingModelProperty) .collect(Collectors.toList()); @@ -191,14 +185,15 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService } private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) { - ThingModelProperty property = new ThingModelProperty(); - property.setIdentifier(function.getIdentifier()); - property.setName(function.getName()); - property.setDescription(function.getDescription()); + ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class); property.setDataType(function.getProperty().getDataType()); return property; } + private String getDatabaseName() { + return url.substring(url.lastIndexOf("/") + 1); + } + static String getProductPropertySTableName(Integer deviceType, String productKey) { return switch (deviceType) { case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); @@ -207,7 +202,4 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService }; } - static String getDevicePropertyTableName(String deviceType, String productKey, String deviceKey) { - return String.format("%s_%s_%s", deviceType, productKey, deviceKey).toLowerCase(); - } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index ae7fa51ee7..9bad7613ed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -1,13 +1,16 @@ package cn.iocoder.yudao.module.iot.service.tdengine; +import cn.hutool.core.date.DateUtil; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; @@ -17,9 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @Slf4j @@ -36,11 +37,14 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private IotTdEngineService iotTdEngineService; + @Resource + private DeviceDataRedisDAO deviceDataRedisDAO; + @Override @TenantIgnore public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { // 判断设备状态,如果为未激活状态,创建数据表 - if (device.getStatus().equals(0)) { + if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { // 创建设备数据表 createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); // 更新设备状态 @@ -50,53 +54,99 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ iotDeviceService.updateDeviceStatus(updateReqVO); } - // 1. 获取设备属性 + // 获取设备属性 Map params = thingModelMessage.dataToMap(); - // 2. 物模型校验,过滤非物模型属性 - List thinkModelFunctionListByProductKey = iotThinkModelFunctionService.getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey()); + // 物模型校验,过滤非物模型属性 + List functionList = iotThinkModelFunctionService + .getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey()) + .stream() + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType())) + .toList(); - // 2.1 筛选是属性 IotProductFunctionTypeEnum - thinkModelFunctionListByProductKey.removeIf(iotThinkModelFunctionDO -> !iotThinkModelFunctionDO.getType().equals(IotProductFunctionTypeEnum.PROPERTY.getType())); - if (thinkModelFunctionListByProductKey.isEmpty()) { + if (functionList.isEmpty()) { return; } - // 2.2 获取属性名称 - Map thingModelProperties = thinkModelFunctionListByProductKey.stream().collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, IotThinkModelFunctionDO::getName)); - // 4. 保存属性记录 + // 获取属性标识符集合 + Set propertyIdentifiers = functionList.stream() + .map(IotThinkModelFunctionDO::getIdentifier) + .collect(Collectors.toSet()); + + Map functionMap = functionList.stream() + .collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, function -> function)); + + // 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); - - // 1. 设置字段名 schemaFieldValues.add(new TdFieldDO("time", thingModelMessage.getTime())); - - // 2. 遍历新属性 params.forEach((key, val) -> { - if (thingModelProperties.containsKey(key)) { + if (propertyIdentifiers.contains(key)) { schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); + // 缓存设备属性 + setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime()); } }); + if (schemaFieldValues.size() == 1) { + return; + } - // 3. 保存设备属性 + // 构建并保存设备属性 TableDO tableData = new TableDO(); - tableData.setDataBaseName(url.substring(url.lastIndexOf("/") + 1)); + tableData.setDataBaseName(getDatabaseName()); tableData.setSuperTableName(getProductPropertySTableName(device.getDeviceType(), device.getProductKey())); - tableData.setTableName("device_" + device.getProductKey().toLowerCase() + "_" + device.getDeviceName().toLowerCase()); + tableData.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); tableData.setSchemaFieldValues(schemaFieldValues); - // 4. 保存数据 iotTdEngineService.insertData(tableData); } + /** + * 缓存设备属性 + * + * @param device 设备信息 + * @param iotThinkModelFunctionDO 物模型属性 + * @param val 属性值 + * @param time 时间 + */ + private void setDeviceDataCache(IotDeviceDO device, IotThinkModelFunctionDO iotThinkModelFunctionDO, Object val, Long time) { + IotDeviceDataDO deviceData = IotDeviceDataDO.builder() + .productKey(device.getProductKey()) + .deviceName(device.getDeviceName()) + .identifier(iotThinkModelFunctionDO.getIdentifier()) + .value(val != null ? val.toString() : null) + .updateTime(DateUtil.toLocalDateTime(new Date(time))) + .deviceId(device.getId()) + .thinkModelFunctionId(iotThinkModelFunctionDO.getId()) + .name(iotThinkModelFunctionDO.getName()) + .dataType(iotThinkModelFunctionDO.getProperty().getDataType().getType()) + .build(); + deviceDataRedisDAO.set(deviceData); + } + + /** + * 创建设备数据表 + * + * @param deviceType 设备类型 + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @param deviceKey 设备 Key + */ private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) { + String superTableName = getProductPropertySTableName(deviceType, productKey); + String dataBaseName = getDatabaseName(); + + List> maps = iotTdEngineService.describeSuperTable(dataBaseName, superTableName); List tagsFieldValues = new ArrayList<>(); - String SuperTableName = getProductPropertySTableName(deviceType, productKey); - List> maps = iotTdEngineService.describeSuperTable(url.substring(url.lastIndexOf("/") + 1), SuperTableName); + if (maps != null) { - List> taggedNotesList = maps.stream().filter(map -> "TAG".equals(map.get("note"))).toList(); + List> taggedNotesList = maps.stream() + .filter(map -> "TAG".equals(map.get("note"))) + .toList(); + tagsFieldValues = FieldParser.parse(taggedNotesList.stream() .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) .collect(Collectors.toList())); + for (TdFieldDO tagsFieldValue : tagsFieldValues) { switch (tagsFieldValue.getFieldName()) { case "product_key" -> tagsFieldValue.setFieldValue(productKey); @@ -107,21 +157,49 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ } } - // 1. 创建设备数据表 - String tableName = "device_" + productKey.toLowerCase() + "_" + deviceName.toLowerCase(); + // 创建设备数据表 + String tableName = getDeviceTableName(productKey, deviceName); TableDO tableDto = new TableDO(); - tableDto.setDataBaseName(url.substring(url.lastIndexOf("/") + 1)); - tableDto.setSuperTableName(SuperTableName); + tableDto.setDataBaseName(dataBaseName); + tableDto.setSuperTableName(superTableName); tableDto.setTableName(tableName); tableDto.setTagsFieldValues(tagsFieldValues); + iotTdEngineService.createTable(tableDto); } - static String getProductPropertySTableName(Integer deviceType, String productKey) { + /** + * 获取数据库名称 + * + * @return 数据库名称 + */ + private String getDatabaseName() { + return url.substring(url.lastIndexOf("/") + 1); + } + + /** + * 获取产品属性表名 + * + * @param deviceType 设备类型 + * @param productKey 产品 Key + * @return 产品属性表名 + */ + private static String getProductPropertySTableName(Integer deviceType, String productKey) { return switch (deviceType) { case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); case 2 -> String.format("gateway_%s", productKey).toLowerCase(); default -> String.format("device_%s", productKey).toLowerCase(); }; } + + /** + * 获取设备表名 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @return 设备表名 + */ + private static String getDeviceTableName(String productKey, String deviceName) { + return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); + } } \ No newline at end of file From d7b8cf547f4d96b49c3371fbbf4535a1c4dfab3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Tue, 5 Nov 2024 23:26:34 +0800 Subject: [PATCH 009/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E8=AE=BE=E5=A4=87=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/domain/visual/SelectVisualDto.java | 29 +++++++------ .../admin/device/IotDeviceDataController.java | 10 +++++ .../vo/deviceData/IotDeviceDataReqVO.java | 12 +++++- .../vo/deviceData/IotTimeDataRespVO.java} | 8 ++-- .../iot/dal/tdengine/TdEngineMapper.java | 7 ++- .../service/device/IotDeviceDataService.java | 10 +++++ .../device/IotDeviceDataServiceImpl.java | 43 +++++++++++++++++++ .../mapper/tdengine/TdEngineMapper.xml | 19 +++++--- 8 files changed, 112 insertions(+), 26 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/{dal/dataobject/tdengine/TimeData.java => controller/admin/device/vo/deviceData/IotTimeDataRespVO.java} (67%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java index 1444da9727..071c763034 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java @@ -4,24 +4,24 @@ import lombok.Data; import java.util.Map; -/** - * @ClassDescription: 查询可视化所需入参对象 - * @ClassName: SelectDto - * @Author: andyz - * @Date: 2022-07-29 14:12:26 - * @Version 1.0 - */ @Data public class SelectVisualDto { -// @NotBlank(message = "invalid operation: tableName can not be empty") + /** + * 数据库名称 + */ private String dataBaseName; -// @NotBlank(message = "invalid operation: tableName can not be empty") + /** + * 表名 + */ private String tableName; -// @NotBlank(message = "invalid operation: fieldName can not be empty") //属性 + /** + * 属性 + */ private String fieldName; + /** * 查询类型,0历史数据,1实时数据,2聚合数据 */ @@ -39,10 +39,15 @@ public class SelectVisualDto { * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天 */ private String interval; - // @NotNull(message = "invalid operation: startTime can not be null") + + /** + * 开始时间 + */ private Long startTime; - // @NotNull(message = "invalid operation: endTime can not be null") + /** + * 结束时间 + */ private Long endTime; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 93b368bd8d..e710d0ea8f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -35,4 +38,11 @@ public class IotDeviceDataController { return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); } + @GetMapping("/history-data") + @Operation(summary = "获取设备属性历史数据") + public CommonResult> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) { + PageResult> list = deviceDataService.getDevicePropertiesHistoryData(deviceDataReqVO); + return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java index b8a95f607e..5f87a1cb82 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java @@ -1,13 +1,18 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; +import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + @Schema(description = "管理后台 - IoT 设备数据 Request VO") @Data -public class IotDeviceDataReqVO { +public class IotDeviceDataReqVO extends PageParam { @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") private Long deviceId; @@ -18,4 +23,9 @@ public class IotDeviceDataReqVO { @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) private String name; + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Size(min = 2, max = 2, message = "请选择时间范围") + private LocalDateTime[] times; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotTimeDataRespVO.java similarity index 67% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotTimeDataRespVO.java index f7e251ea8a..1bf085c544 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TimeData.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotTimeDataRespVO.java @@ -1,16 +1,14 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -/** - * 统计的时间数据 - */ + @Data @NoArgsConstructor @AllArgsConstructor -public class TimeData { +public class IotTimeDataRespVO { /** * 时间 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java index 94db527c28..c126946f1d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java @@ -88,6 +88,7 @@ public interface TdEngineMapper { /** * 创建表 - 创建超级表的子表 + * * @param tableDO 表信息 */ @InterceptorIgnore(tenantLine = "true") @@ -114,6 +115,7 @@ public interface TdEngineMapper { Map getLastData(SelectDto selectDto); + @InterceptorIgnore(tenantLine = "true") List> getHistoryData(SelectVisualDto selectVisualDto); List> getRealtimeData(SelectVisualDto selectVisualDto); @@ -122,5 +124,6 @@ public interface TdEngineMapper { List> getLastDataByTags(TagsSelectDao tagsSelectDao); - -} + @InterceptorIgnore(tenantLine = "true") + Long getHistoryCount(SelectVisualDto selectVisualDto); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java index f67f426855..96181fd263 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import jakarta.validation.Valid; import java.util.List; +import java.util.Map; /** * IoT 设备数据 Service 接口 @@ -30,4 +32,12 @@ public interface IotDeviceDataService { * @return 设备属性最新数据 */ List getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId); + + /** + * 获得设备属性历史数据 + * + * @param deviceDataReqVO 设备属性历史数据 Request VO + * @return 设备属性历史数据 + */ + PageResult> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 86ac630c6f..1b44bd5d0a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -1,27 +1,38 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.core.date.DateUtil; import cn.hutool.json.JSONObject; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; +import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.time.ZoneId; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service public class IotDeviceDataServiceImpl implements IotDeviceDataService { + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") + private String url; + @Resource private IotDeviceService deviceService; @Resource @@ -32,6 +43,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + @Resource + private TdEngineMapper tdEngineMapper; + @Override public void saveDeviceData(String productKey, String deviceName, String message) { // 1. 根据产品 key 和设备名称,获得设备信息 @@ -91,4 +105,33 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { }); return list; } + + @Override + public PageResult> getDevicePropertiesHistoryData(IotDeviceDataReqVO deviceDataReqVO) { + PageResult> pageResult = new PageResult<>(); + // 1. 获取设备信息 + IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); + // 2. 获取设备属性历史数据 + SelectVisualDto selectVisualDto = new SelectVisualDto(); + selectVisualDto.setDataBaseName(getDatabaseName()); + selectVisualDto.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); + selectVisualDto.setFieldName(deviceDataReqVO.getIdentifier()); + selectVisualDto.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); + selectVisualDto.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); + Map params = new HashMap<>(); + params.put("rows", deviceDataReqVO.getPageSize()); + params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); + selectVisualDto.setParams(params); + pageResult.setList(tdEngineMapper.getHistoryData(selectVisualDto)); + pageResult.setTotal(tdEngineMapper.getHistoryCount(selectVisualDto)); + return pageResult; + } + + private String getDatabaseName() { + return url.substring(url.lastIndexOf("/") + 1); + } + + private static String getDeviceTableName(String productKey, String deviceName) { + return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); + } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml index 9d5cf2a43e..4e127e44b1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml @@ -292,17 +292,18 @@ + From e3dcea9cb3792bfebb489c3ccfb568bcec6bf7da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Fri, 8 Nov 2024 23:07:37 +0800 Subject: [PATCH 010/309] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E4=BC=98=E5=8C=96=20tdengine=20=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=95=B0=E6=8D=AE=E5=BA=93=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/domain/BaseEntity.java | 18 - .../iot/domain/DeviceDataExportExcelDto.java | 35 -- .../yudao/module/iot/domain/DeviceDataVo.java | 19 - .../yudao/module/iot/domain/Fields.java | 65 ---- .../yudao/module/iot/domain/FieldsVo.java | 31 -- .../module/iot/domain/IotSequential.java | 71 ---- .../module/iot/domain/MessageCountVo.java | 25 -- .../iot/domain/ProductSuperTableModel.java | 29 -- .../yudao/module/iot/domain/SelectDto.java | 30 -- .../module/iot/domain/SuperTableDto.java | 31 -- .../yudao/module/iot/domain/TableDto.java | 26 -- .../module/iot/domain/TagsSelectDao.java | 25 -- .../yudao/module/iot/domain/Weather.java | 73 ---- .../iot/dal/dataobject/tdengine/SelectDO.java | 51 +++ .../dataobject/tdengine/SelectVisualDO.java} | 4 +- .../iot/dal/dataobject/tdengine/TableDO.java | 27 -- .../dal/dataobject/tdengine/TableData.java | 44 --- .../dal/dataobject/tdengine/TagsSelectDO.java | 36 ++ .../dal/dataobject/tdengine/TdFieldDO.java | 10 +- .../dal/dataobject/tdengine/TdTableDO.java | 65 ++++ .../tdengine/TdEngineDataWriterMapper.java | 25 ++ .../dal/tdengine/TdEngineDatabaseMapper.java | 23 ++ .../iot/dal/tdengine/TdEngineMapper.java | 129 ------- .../iot/dal/tdengine/TdEngineQueryMapper.java | 85 +++++ .../tdengine/TdEngineSuperTableMapper.java | 111 ++++++ .../iot/dal/tdengine/TdEngineTableMapper.java | 41 +++ .../device/IotDeviceDataServiceImpl.java | 27 +- ...Service.java => IotSuperTableService.java} | 15 +- ...mpl.java => IotSuperTableServiceImpl.java} | 309 ++++++++++------- .../service/tdengine/IotTdEngineService.java | 142 -------- .../tdengine/IotTdEngineServiceImpl.java | 107 ------ .../tdengine/IotThingModelMessageService.java | 4 +- .../IotThingModelMessageServiceImpl.java | 38 +- .../tdengine/TdEngineDataWriterService.java | 19 + .../TdEngineDataWriterServiceImpl.java | 21 ++ .../tdengine/TdEngineDatabaseService.java | 14 + .../tdengine/TdEngineDatabaseServiceImpl.java | 22 ++ .../tdengine/TdEngineQueryService.java | 28 ++ .../tdengine/TdEngineQueryServiceImpl.java | 26 ++ .../tdengine/TdEngineSuperTableService.java | 123 +++++++ .../TdEngineSuperTableServiceImpl.java | 90 +++++ .../tdengine/TdEngineTableService.java | 21 ++ .../tdengine/TdEngineTableServiceImpl.java | 23 ++ .../IotThinkModelFunctionService.java | 2 +- .../IotThinkModelFunctionServiceImpl.java | 4 +- .../tdengine/TdEngineDataWriterMapper.xml | 22 ++ .../tdengine/TdEngineDatabaseMapper.xml | 13 + .../mapper/tdengine/TdEngineMapper.xml | 327 ------------------ .../mapper/tdengine/TdEngineQueryMapper.xml | 73 ++++ .../tdengine/TdEngineSuperTableMapper.xml | 72 ++++ .../mapper/tdengine/TdEngineTableMapper.xml | 33 ++ 51 files changed, 1262 insertions(+), 1442 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java rename yudao-module-iot/{yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java => yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java} (90%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{IotDbStructureDataService.java => IotSuperTableService.java} (50%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{IotDbStructureDataServiceImpl.java => IotSuperTableServiceImpl.java} (55%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java deleted file mode 100644 index 496be9a247..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/BaseEntity.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -@Data -public class BaseEntity { - private static final long serialVersionUID = 1L; - - /** - * 数据库名称 - */ - private String dataBaseName; - - /** - * 超级表名称 - */ - private String superTableName; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java deleted file mode 100644 index cedb4d1190..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataExportExcelDto.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -/** - * 设备数据导出 excel DTO - */ -@Data -public class DeviceDataExportExcelDto { - - /** - * 设备标识 - */ - private String deviceKey; - - /** - * 导出形式 1 单个参数导出 2 全部参数导出 - */ - private String exportType; - - /** - * 导出开始时间 - */ - private String exportBeginTime; - - /** - * 导出结束时间 - */ - private String exportEndTime; - - /** - * 导出参数,空则导出全部 - */ - private String exportParameter; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java deleted file mode 100644 index b9f6fc4127..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/DeviceDataVo.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -/** - * @ClassDescription: 查询可视化所需入参对象 - * @ClassName: SelectDto - * @Author: andyz - * @Date: 2022-07-29 14:12:26 - * @Version 1.0 - */ -@Data -public class DeviceDataVo { - - - private String deviceId; - - private Long lastTime; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java deleted file mode 100644 index ec0a7f1483..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Fields.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -@Data -public class Fields { - private static final long serialVersionUID = 1L; - - /** - * 字段名称 - */ - private String fieldName; - - /** - * 字段值 - */ - private Object fieldValue; - - /** - * 字段数据类型 - */ -// private DataTypeEnum dataType; - - /** - * 字段字节大小 - */ - private Integer size; - - public Fields() { - } - - public Fields(String fieldName, String dataType, Integer size) { -// this.fieldName = fieldName; -// //根据规则匹配字段数据类型 -// switch (dataType.toLowerCase()) { -// case ("json"): -// this.dataType = DataTypeEnum.JSON; -// this.size = size; -// break; -// case ("string"): -// this.dataType = DataTypeEnum.NCHAR; -// this.size = size; -// break; -// case ("binary"): -// this.dataType = DataTypeEnum.BINARY; -// this.size = size; -// break; -// case ("int"): -// this.dataType = DataTypeEnum.INT; -// break; -// case ("bool"): -// this.dataType = DataTypeEnum.BOOL; -// break; -// case ("decimal"): -// this.dataType = DataTypeEnum.DOUBLE; -// break; -// case ("timestamp"): -// if ("eventTime".equals(fieldName)) { -// this.fieldName = "eventTime"; -// } -// this.dataType = DataTypeEnum.TIMESTAMP; -// break; -// } - } -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java deleted file mode 100644 index fc86a8f7ee..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/FieldsVo.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Builder; -import lombok.Data; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; - -/** - * 字段信息 VO - */ -@Data -@Builder -public class FieldsVo { - - /** - * 字段名称 - */ - private String fieldName; - - /** - * 字段数据类型 - */ - private String dataType; - - /** - * 字段字节大小 - */ - private Integer size; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java deleted file mode 100644 index 349d6d1371..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/IotSequential.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import com.fasterxml.jackson.annotation.JsonFormat; - -import java.sql.Timestamp; - -public class IotSequential extends BaseEntity { - private static final long serialVersionUID = 1L; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") - private Timestamp statetime; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") - private Timestamp endtime; - - private String deviceid; - - private String eventtime; - - private String serviceid; - - private String devices; - - public String getDeviceid() { - return deviceid; - } - - public void setDeviceid(String deviceid) { - this.deviceid = deviceid; - } - - public String getEventtime() { - return eventtime; - } - - public void setEventtime(String eventtime) { - this.eventtime = eventtime; - } - - public String getServiceid() { - return serviceid; - } - - public void setServiceid(String serviceid) { - this.serviceid = serviceid; - } - - public String getDevices() { - return devices; - } - - public void setDevices(String devices) { - this.devices = devices; - } - - public Timestamp getStatetime() { - return statetime; - } - - public void setStatetime(Timestamp statetime) { - this.statetime = statetime; - } - - public Timestamp getEndtime() { - return endtime; - } - - public void setEndtime(Timestamp endtime) { - this.endtime = endtime; - } -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java deleted file mode 100644 index e9e93cac01..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/MessageCountVo.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 统计的时间数据 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class MessageCountVo { - - /** - * 时间 - */ - private String time; - - /** - * 数据值 - */ - private Object data; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java deleted file mode 100644 index c5989811fa..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/ProductSuperTableModel.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import com.fasterxml.jackson.annotation.JsonFormat; -import lombok.Data; - -import java.sql.Timestamp; -import java.util.HashMap; -import java.util.Optional; - -@Data -public class ProductSuperTableModel { - private static final long serialVersionUID = 1L; - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone = "GMT+8") - private Timestamp ts; - - private String superTableName; - - /** - * columnsName,columnsProperty - */ - private HashMap columns; - - /** - * tagsName,tagsProperty - */ - private HashMap tags; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java deleted file mode 100644 index c20e5ab5ee..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SelectDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -import java.util.Set; - -@Data -public class SelectDto { - - // @NotBlank(message = "invalid operation: dataBaseName can not be empty") - private String dataBaseName; - -// @NotBlank(message = "invalid operation: tableName can not be empty") - private String tableName; - - // @NotBlank(message = "invalid operation: fieldName can not be empty") - private String fieldName; - - // @NotNull(message = "invalid operation: startTime can not be null") - private Long startTime; - - // @NotNull(message = "invalid operation: endTime can not be null") - private Long endTime; - - private String type; - - private Set orgIds; - - private String deviceId; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java deleted file mode 100644 index a15f135cfb..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/SuperTableDto.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -import java.util.List; - -@Data -public class SuperTableDto extends BaseEntity { - - /** - * 超级表的表结构(业务相关) - * 第一个字段的数据类型必须为timestamp - * 字符相关数据类型必须指定大小 - * 字段名称和字段数据类型不能为空 - */ -// @NotEmpty(message = "invalid operation: schemaFields can not be empty") - private List schemaFields; - - /** - * 超级表的标签字段,可以作为子表在超级表里的标识 - * 字符相关数据类型必须指定大小 - * 字段名称和字段数据类型不能为空 - */ -// @NotEmpty(message = "invalid operation: tagsFields can not be empty") - private List tagsFields; - - /** - * 字段信息对象,超级表添加列时使用该属性 - */ - private Fields fields; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java deleted file mode 100644 index 5ffe9f84e4..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TableDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - -import java.util.List; - -@Data -public class TableDto extends BaseEntity { - - /** - * 超级表普通列字段的值 - * 值需要与创建超级表时普通列字段的数据类型对应上 - */ - private List schemaFieldValues; - - /** - * 超级表标签字段的值 - * 值需要与创建超级表时标签字段的数据类型对应上 - */ - private List tagsFieldValues; - - /** - * 表名称 - */ - private String tableName; -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java deleted file mode 100644 index 8833b0b50b..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/TagsSelectDao.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import lombok.Data; - - -@Data -public class TagsSelectDao { - - // @NotBlank(message = "invalid operation: dataBaseName can not be empty") - private String dataBaseName; - - // @NotBlank(message = "invalid operation: stableName can not be empty") - private String stableName; - - // @NotBlank(message = "invalid operation: tagsName can not be empty") - private String tagsName; - - // @NotNull(message = "invalid operation: startTime can not be null") - private Long startTime; - - // @NotNull(message = "invalid operation: endTime can not be null") - private Long endTime; - - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java deleted file mode 100644 index a4a1e983b8..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/Weather.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.iot.domain; - -import com.fasterxml.jackson.annotation.JsonFormat; - -import java.sql.Timestamp; - -public class Weather { - - @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS" , timezone = "GMT+8") - private Timestamp ts; - private Float temperature; - private Float humidity; - private String location; - private String note; - private int groupId; - - public Weather() { - } - - public Weather(Timestamp ts, float temperature, float humidity) { - this.ts = ts; - this.temperature = temperature; - this.humidity = humidity; - } - - public Timestamp getTs() { - return ts; - } - - public void setTs(Timestamp ts) { - this.ts = ts; - } - - public Float getTemperature() { - return temperature; - } - - public void setTemperature(Float temperature) { - this.temperature = temperature; - } - - public Float getHumidity() { - return humidity; - } - - public void setHumidity(Float humidity) { - this.humidity = humidity; - } - - public String getLocation() { - return location; - } - - public void setLocation(String location) { - this.location = location; - } - - public int getGroupId() { - return groupId; - } - - public void setGroupId(int groupId) { - this.groupId = groupId; - } - - public String getNote() { - return note; - } - - public void setNote(String note) { - this.note = note; - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java new file mode 100644 index 0000000000..503b26310e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.Data; + +import java.util.Set; +/** + * 查询DO + */ +@Data +public class SelectDO { + + /** + * 数据库名称 + */ + private String dataBaseName; + + /** + * 超级表名称 + */ + private String tableName; + + /** + * 查询字段 + */ + private String fieldName; + + /** + * 开始时间 + */ + private Long startTime; + + /** + * 结束时间 + */ + private Long endTime; + + /** + * 查询类型 + */ + private String type; + + /** + * 查询条件 + */ + private Set orgIds; + + /** + * 设备ID + */ + private String deviceId; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java similarity index 90% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index 071c763034..d5faf45a9c 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/domain/visual/SelectVisualDto.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.domain.visual; +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import lombok.Data; import java.util.Map; @Data -public class SelectVisualDto { +public class SelectVisualDO { /** * 数据库名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java deleted file mode 100644 index 11751ff643..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableDO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import cn.iocoder.yudao.module.iot.domain.BaseEntity; -import lombok.Data; - -import java.util.List; - -@Data -public class TableDO extends BaseEntity { - - /** - * 超级表普通列字段的值 - * 值需要与创建超级表时普通列字段的数据类型对应上 - */ - private List schemaFieldValues; - - /** - * 超级表标签字段的值 - * 值需要与创建超级表时标签字段的数据类型对应上 - */ - private List tagsFieldValues; - - /** - * 表名称 - */ - private String tableName; -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java deleted file mode 100644 index dfb3ed2129..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableData.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.Data; - -import java.util.List; - -/** - * TableData 类用于存储和操作 TDengine 表数据 - */ -@Data -public class TableData { - - /** - * 超级表普通列字段的名称 - */ - private List schemaFieldList; - - /** - * 超级表普通列字段的值 - * 值需要与创建超级表时普通列字段的数据类型对应上 - */ - private List schemaValueList; - - /** - * 超级表标签字段的名称 - */ - private List tagsFieldList; - - /** - * 超级表标签字段的值 - * 值需要与创建超级表时标签字段的数据类型对应上 - */ - private List tagsValueList; - - /** - * 表名称 - */ - private String tableName; - - /** - * 超级表名称 - */ - private String superTableName; -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java new file mode 100644 index 0000000000..9fae91599d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.Data; + +/** + * tags查询DO + */ +@Data +public class TagsSelectDO { + + /** + * 数据库名称 + */ + private String dataBaseName; + + /** + * 超级表名称 + */ + private String stableName; + + /** + * tags名称 + */ + private String tagsName; + + /** + * tags值 + */ + private Long startTime; + + /** + * tags值 + */ + private Long endTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index 7656527489..2e17515095 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java @@ -19,11 +19,6 @@ public class TdFieldDO { */ private String fieldName; - /** - * 字段值 - */ - private Object fieldValue; - /** * 字段类型 */ @@ -34,6 +29,11 @@ public class TdFieldDO { */ private Integer dataLength = 0; + /** + * 字段值 + */ + private Object fieldValue; + public TdFieldDO(String fieldName, String dataType, Integer dataLength) { this.fieldName = fieldName; this.dataType = dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java new file mode 100644 index 0000000000..314c51de3c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * TD 引擎的数据库 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TdTableDO { + + /** + * 数据库名称 + */ + private String dataBaseName; + + /** + * 超级表名称 + */ + private String superTableName; + + /** + * 表名称 + */ + private String tableName; + + /** + * COLUMN 字段 + */ + private TdFieldDO column; + + /** + * TAG 字段 + */ + private TdFieldDO tag; + + /** + * COLUMN 字段 - 列表 + */ + private List columns; + + /** + * TAG 字段 - 列表 + */ + private List tags; + + public TdTableDO(String dataBaseName, String superTableName, List columns, List tags) { + this.dataBaseName = dataBaseName; + this.superTableName = superTableName; + this.columns = columns; + this.tags = tags; + } + + public TdTableDO(String dataBaseName, String superTableName) { + this.dataBaseName = dataBaseName; + this.superTableName = superTableName; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java new file mode 100644 index 0000000000..51f4229a0f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; + +/** + * TD 引擎的数据写入 Mapper + */ +@Mapper +@DS("tdengine") +public interface TdEngineDataWriterMapper { + + /** + * 插入数据 - 指定列插入数据 + * + * @param table 数据 + * dataBaseName 数据库名 + * tableName 表名 + * columns 列 + */ + @InterceptorIgnore(tenantLine = "true") + void insertData(TdTableDO table); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java new file mode 100644 index 0000000000..bc562ccca7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * TD 引擎的数据库 Mapper + */ +@Mapper +@DS("tdengine") +public interface TdEngineDatabaseMapper { + + /** + * 创建数据库 + * SQL:CREATE DATABASE [IF NOT EXISTS] db_name [database_options]; + * + * @param dataBaseName 数据库名称 + */ + @InterceptorIgnore(tenantLine = "true") + void createDatabase(@Param("dataBaseName") String dataBaseName); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java deleted file mode 100644 index c126946f1d..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineMapper.java +++ /dev/null @@ -1,129 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.domain.FieldsVo; -import cn.iocoder.yudao.module.iot.domain.SelectDto; -import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; -import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; -import com.baomidou.dynamic.datasource.annotation.DS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; -import java.util.Map; - -@Mapper -@DS("tdengine") -public interface TdEngineMapper { - - /** - * 创建数据库 - * - * @param dataBaseName 数据库名称 - */ - @InterceptorIgnore(tenantLine = "true") - void createDatabase(@Param("dataBaseName") String dataBaseName); - - /** - * 创建超级表 - * - * @param schemaFields schema字段 - * @param tagsFields tags字段 - * @param dataBaseName 数据库名称 - * @param superTableName 超级表名称 - */ - @InterceptorIgnore(tenantLine = "true") - void createSuperTable(@Param("schemaFields") List schemaFields, - @Param("tagsFields") List tagsFields, - @Param("dataBaseName") String dataBaseName, - @Param("superTableName") String superTableName); - - /** - * 查看超级表 - 显示当前数据库下的所有超级表信息 - * SQL:SHOW STABLES [LIKE tb_name_wildcard]; - * - * @param dataBaseName 数据库名称 - * @param superTableName 超级表名称 - */ - @InterceptorIgnore(tenantLine = "true") - List> showSuperTables(@Param("dataBaseName") String dataBaseName, - @Param("superTableName") String superTableName); - - /** - * 查看超级表 - 获取超级表的结构信息 - * SQL:DESCRIBE [db_name.]stb_name; - *

- * * @param dataBaseName 数据库名称 - * * @param superTableName 超级表名称 - */ - @InterceptorIgnore(tenantLine = "true") - List> describeSuperTable(@Param("dataBaseName") String dataBaseName, - @Param("superTableName") String superTableName); - - /** - * 为超级表添加列 - * - * @param dataBaseName 数据库名称 - * @param superTableName 超级表名称 - * @param field 字段信息 - */ - @InterceptorIgnore(tenantLine = "true") - void addColumnForSuperTable(@Param("dataBaseName") String dataBaseName, - @Param("superTableName") String superTableName, - @Param("field") TdFieldDO field); - - /** - * 为超级表删除列 - * - * @param dataBaseName 数据库名称 - * @param superTableName 超级表名称 - * @param field 字段信息 - */ - @InterceptorIgnore(tenantLine = "true") - void dropColumnForSuperTable(@Param("dataBaseName") String dataBaseName, - @Param("superTableName") String superTableName, - @Param("field") TdFieldDO field); - - /** - * 创建表 - 创建超级表的子表 - * - * @param tableDO 表信息 - */ - @InterceptorIgnore(tenantLine = "true") - void createTable(TableDO tableDO); - - /** - * 插入数据 - 指定列插入数据 - * - * @param tableDto 数据 - */ - @InterceptorIgnore(tenantLine = "true") - void insertData(TableDO tableDto); - - List> selectByTimestamp(SelectDto selectDto); - - - void addTagForSuperTable(@Param("superTableName") String superTableName, - @Param("fieldsVo") FieldsVo fieldsVo); - - void dropTagForSuperTable(@Param("superTableName") String superTableName, - @Param("fieldsVo") FieldsVo fieldsVo); - - Map getCountByTimestamp(SelectDto selectDto); - - Map getLastData(SelectDto selectDto); - - @InterceptorIgnore(tenantLine = "true") - List> getHistoryData(SelectVisualDto selectVisualDto); - - List> getRealtimeData(SelectVisualDto selectVisualDto); - - List> getAggregateData(SelectVisualDto selectVisualDto); - - List> getLastDataByTags(TagsSelectDao tagsSelectDao); - - @InterceptorIgnore(tenantLine = "true") - Long getHistoryCount(SelectVisualDto selectVisualDto); -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java new file mode 100644 index 0000000000..e5353f7ee9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * TD 引擎的查询 Mapper + */ +@Mapper +@DS("tdengine") +public interface TdEngineQueryMapper { + + /** + * 根据时间戳查询数据 + * + * @param selectDO 查询条件 + * @return 查询结果 + */ + List> selectByTimestamp(SelectDO selectDO); + + /** + * 根据时间戳获取数据条数 + * + * @param selectDO 查询条件 + * @return 数据条数 + */ + Map getCountByTimestamp(SelectDO selectDO); + + /** + * 获取最新数据 + * + * @param selectDO 查询条件 + * @return 最新数据 + */ + Map getLastData(SelectDO selectDO); + + /** + * 获取历史数据 + * + * @param selectVisualDO 查询条件 + * @return 历史数据列表 + */ + @InterceptorIgnore(tenantLine = "true") + List> getHistoryData(SelectVisualDO selectVisualDO); + + /** + * 获取实时数据 + * + * @param selectVisualDO 查询条件 + * @return 实时数据列表 + */ + List> getRealtimeData(SelectVisualDO selectVisualDO); + + /** + * 获取聚合数据 + * + * @param selectVisualDO 查询条件 + * @return 聚合数据列表 + */ + List> getAggregateData(SelectVisualDO selectVisualDO); + + /** + * 根据标签获取最新数据 + * + * @param tagsSelectDO 查询条件 + * @return 最新数据列表 + */ + List> getLastDataByTags(TagsSelectDO tagsSelectDO); + + /** + * 获取历史数据条数 + * + * @param selectVisualDO 查询条件 + * @return 数据条数 + */ + @InterceptorIgnore(tenantLine = "true") + Long getHistoryCount(SelectVisualDO selectVisualDO); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java new file mode 100644 index 0000000000..e688563d5f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * TD 引擎的超级表 Mapper + */ +@Mapper +@DS("tdengine") +public interface TdEngineSuperTableMapper { + + /** + * 创建超级表 + * SQL:CREATE STABLE [IF NOT EXISTS] stb_name (create_definition [, create_definition] ...) TAGS (create_definition [, create_definition] ...) [table_options]; + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * columns 列信息 + * tags 标签信息 + */ + @InterceptorIgnore(tenantLine = "true") + void createSuperTable(TdTableDO superTable); + + /** + * 查看超级表 - 显示当前数据库下的所有超级表信息 + * SQL:SHOW STABLES [LIKE tb_name_wildcard]; + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + List> showSuperTables(TdTableDO superTable); + + /** + * 查看超级表 - 获取超级表的结构信息 + * SQL:DESCRIBE [db_name.]stb_name; + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + */ + @InterceptorIgnore(tenantLine = "true") + List> describeSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 增加列 + * SQL:ALTER STABLE stb_name ADD COLUMN col_name column_type; + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * column 列信息 + */ + @InterceptorIgnore(tenantLine = "true") + void addColumnForSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 删除列 + * SQL:ALTER STABLE stb_name DROP COLUMN col_name; + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * column 列信息 + */ + @InterceptorIgnore(tenantLine = "true") + void dropColumnForSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 修改列宽 + * SQL:ALTER STABLE stb_name MODIFY COLUMN col_name data_type(length); + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * column 列信息 + */ + @InterceptorIgnore(tenantLine = "true") + void modifyColumnWidthForSuperTable(TdTableDO superTable); + + + /** + * 修改超级表 - 为超级表添加标签 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tag 标签信息 + */ + @InterceptorIgnore(tenantLine = "true") + void addTagForSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 为超级表删除标签 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tag 标签信息 + */ + @InterceptorIgnore(tenantLine = "true") + void dropTagForSuperTable(TdTableDO superTable); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java new file mode 100644 index 0000000000..72517bcaba --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; + +/** + * TD 引擎的表 Mapper + */ +@Mapper +@DS("tdengine") +public interface TdEngineTableMapper { + + /** + * 创建子表 - 创建子表 + * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...); + * + * @param table 表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tableName 子表名称 + * tags TAG 字段 + */ + @InterceptorIgnore(tenantLine = "true") + void createTable(TdTableDO table); + + /** + * 创建子表 - 创建子表并指定标签的值 + * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...); + * + * @param table 表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tableName 子表名称 + * tags TAG 字段 + */ + @InterceptorIgnore(tenantLine = "true") + void createTableWithTags(TdTableDO table); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 1b44bd5d0a..4179075dfb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -6,13 +6,13 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; -import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; +import cn.iocoder.yudao.module.iot.service.tdengine.TdEngineQueryService; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -39,13 +39,12 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { private IotThingModelMessageService thingModelMessageService; @Resource private IotThinkModelFunctionService thinkModelFunctionService; + @Resource + private TdEngineQueryService tdEngineQueryService; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; - @Resource - private TdEngineMapper tdEngineMapper; - @Override public void saveDeviceData(String productKey, String deviceName, String message) { // 1. 根据产品 key 和设备名称,获得设备信息 @@ -112,18 +111,18 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性历史数据 - SelectVisualDto selectVisualDto = new SelectVisualDto(); - selectVisualDto.setDataBaseName(getDatabaseName()); - selectVisualDto.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); - selectVisualDto.setFieldName(deviceDataReqVO.getIdentifier()); - selectVisualDto.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); - selectVisualDto.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); + SelectVisualDO selectVisualDO = new SelectVisualDO(); + selectVisualDO.setDataBaseName(getDatabaseName()); + selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); + selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier()); + selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); + selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); Map params = new HashMap<>(); params.put("rows", deviceDataReqVO.getPageSize()); params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); - selectVisualDto.setParams(params); - pageResult.setList(tdEngineMapper.getHistoryData(selectVisualDto)); - pageResult.setTotal(tdEngineMapper.getHistoryCount(selectVisualDto)); + selectVisualDO.setParams(params); + pageResult.setList(tdEngineQueryService.getHistoryData(selectVisualDO)); + pageResult.setTotal(tdEngineQueryService.getHistoryCount(selectVisualDO)); return pageResult; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java similarity index 50% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java index 42439e25a2..7c88b83aa5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java @@ -1,26 +1,15 @@ package cn.iocoder.yudao.module.iot.service.tdengine; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import java.util.List; /** - * 数据结构接口 + * IoT 超级表服务,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 */ -public interface IotDbStructureDataService { - - /** - * 创建物模型定义 - */ - void createSuperTable(ThingModelRespVO thingModel, Integer deviceType); - - /** - * 更新物模型定义 - */ - void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType); +public interface IotSuperTableService { /** * 创建超级表数据模型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java similarity index 55% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java index 9471781027..3be324c4e6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotDbStructureDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import jakarta.annotation.Resource; @@ -17,135 +18,19 @@ import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; +/** + * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 + */ @Service @Slf4j -public class IotDbStructureDataServiceImpl implements IotDbStructureDataService { +public class IotSuperTableServiceImpl implements IotSuperTableService { @Resource - private IotTdEngineService iotTdEngineService; + private TdEngineSuperTableService tdEngineSuperTableService; @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; - @Override - public void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - // 1. 解析物模型,获得字段列表 - List schemaFields = new ArrayList<>(); - schemaFields.add(TdFieldDO.builder() - .fieldName("time") - .dataType("TIMESTAMP") - .build()); - schemaFields.addAll(FieldParser.parse(thingModel)); - - // 3. 设置超级表的标签 - List tagsFields = Arrays.asList( - TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_type").dataType("INT").build() - ); - - // 4. 获取超级表的名称 - String superTableName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); - - // 5. 创建超级表 - String dataBaseName = getDatabaseName(); - iotTdEngineService.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); - } - - @Override - public void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - try { - String tbName = getProductPropertySTableName(deviceType, thingModel.getProductKey()); - List oldFields = getTableFields(tbName); - List newFields = FieldParser.parse(thingModel); - - updateTableFields(tbName, oldFields, newFields); - } catch (Exception e) { - log.error("更新物模型超级表失败", e); - } - } - - // 获取表字段 - private List getTableFields(String tableName) { - List fields = new ArrayList<>(); - // 获取超级表的描述信息 - List> maps = iotTdEngineService.describeSuperTable(getDatabaseName(), tableName); - if (maps != null) { - // 过滤掉 note 字段为 TAG 的记录和 time 字段 - List> filteredMaps = maps.stream() - .filter(map -> !"TAG".equals(map.get("note"))) - .filter(map -> !"time".equals(map.get("field"))) - .toList(); - // 解析字段信息 - fields = FieldParser.parse(filteredMaps.stream() - .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) - .collect(Collectors.toList())); - } - return fields; - } - - // 更新表字段 - private void updateTableFields(String tableName, List oldFields, List newFields) { - // 获取新增字段 - List addFields = getAddFields(oldFields, newFields); - // 获取修改字段 - List modifyFields = getModifyFields(oldFields, newFields); - // 获取删除字段 - List dropFields = getDropFields(oldFields, newFields); - - String dataBaseName = getDatabaseName(); - // 添加新增字段 - if (CollUtil.isNotEmpty(addFields)) { - iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, addFields); - } - // 删除旧字段 - if (CollUtil.isNotEmpty(dropFields)) { - iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, dropFields); - } - // 修改字段(先删除再添加) - if (CollUtil.isNotEmpty(modifyFields)) { - iotTdEngineService.dropColumnForSuperTable(dataBaseName, tableName, modifyFields); - iotTdEngineService.addColumnForSuperTable(dataBaseName, tableName, modifyFields); - } - } - - // 获取新增字段 - private List getAddFields(List oldFields, List newFields) { - Set oldFieldNames = oldFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return newFields.stream() - .filter(f -> !oldFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - // 获取修改字段 - private List getModifyFields(List oldFields, List newFields) { - Map oldFieldMap = oldFields.stream() - .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); - - return newFields.stream() - .filter(f -> { - TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); - return oldField != null && - (!oldField.getDataType().equals(f.getDataType()) || - !Objects.equals(oldField.getDataLength(), f.getDataLength())); - }) - .collect(Collectors.toList()); - } - - // 获取删除字段 - private List getDropFields(List oldFields, List newFields) { - Set newFieldNames = newFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return oldFields.stream() - .filter(f -> !"time".equals(f.getFieldName()) && !"device_id".equals(f.getFieldName())) - .filter(f -> !newFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - @Override public void createSuperTableDataModel(IotProductDO product, List functionList) { ThingModelRespVO thingModel = buildThingModel(product, functionList); @@ -155,9 +40,9 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService return; } - String superTableName = getProductPropertySTableName(product.getDeviceType(), product.getProductKey()); - String dataBaseName = getDatabaseName(); - Integer tableExists = iotTdEngineService.checkSuperTableExists(dataBaseName, superTableName); + String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey()); + String databaseName = getDatabaseName(); + Integer tableExists = tdEngineSuperTableService.checkSuperTableExists(new TdTableDO(databaseName, superTableName)); if (tableExists != null && tableExists > 0) { updateSuperTable(thingModel, product.getDeviceType()); @@ -166,40 +51,202 @@ public class IotDbStructureDataServiceImpl implements IotDbStructureDataService } } + /** + * 创建超级表 + */ + private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { + // 解析物模型,获取字段列表 + List schemaFields = new ArrayList<>(); + schemaFields.add(TdFieldDO.builder() + .fieldName("time") + .dataType("TIMESTAMP") + .build()); + schemaFields.addAll(FieldParser.parse(thingModel)); + + // 设置超级表的标签 + List tagsFields = List.of( + TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("device_type").dataType("INT").build() + ); + + // 获取超级表的名称和数据库名称 + String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); + String databaseName = getDatabaseName(); + + // 创建超级表 + tdEngineSuperTableService.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); + } + + /** + * 更新超级表 + */ + private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { + String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); + try { + List oldFields = getTableFields(superTableName); + List newFields = FieldParser.parse(thingModel); + + updateTableFields(superTableName, oldFields, newFields); + } catch (Exception e) { + log.error("更新物模型超级表失败: {}", e.getMessage(), e); + } + } + + /** + * 获取表的字段信息 + */ + private List getTableFields(String tableName) { + List> tableDescription = tdEngineSuperTableService.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); + if (CollUtil.isEmpty(tableDescription)) { + return Collections.emptyList(); + } + + return tableDescription.stream() + .filter(map -> !"TAG".equals(map.get("note"))) + .filter(map -> !"time".equals(map.get("field"))) + .map(map -> TdFieldDO.builder() + .fieldName((String) map.get("field")) + .dataType((String) map.get("type")) + .dataLength((Integer) map.get("length")) + .build()) + .collect(Collectors.toList()); + } + + /** + * 更新表的字段,包括新增、修改和删除字段 + */ + private void updateTableFields(String tableName, List oldFields, List newFields) { + String databaseName = getDatabaseName(); + + // 获取新增、修改、删除的字段 + List addFields = getAddFields(oldFields, newFields); + List modifyFields = getModifyFields(oldFields, newFields); + List dropFields = getDropFields(oldFields, newFields); + + // 添加新增字段 + if (CollUtil.isNotEmpty(addFields)) { + tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .columns(addFields) + .build()); + } + // 删除旧字段 + if (CollUtil.isNotEmpty(dropFields)) { + tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .columns(dropFields) + .build()); + } + // 修改字段(先删除再添加) + if (CollUtil.isNotEmpty(modifyFields)) { + tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .columns(modifyFields) + .build()); + tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .columns(addFields) + .build()); + } + } + + /** + * 获取需要新增的字段 + */ + private List getAddFields(List oldFields, List newFields) { + Set oldFieldNames = oldFields.stream() + .map(TdFieldDO::getFieldName) + .collect(Collectors.toSet()); + return newFields.stream() + .filter(f -> !oldFieldNames.contains(f.getFieldName())) + .collect(Collectors.toList()); + } + + /** + * 获取需要修改的字段 + */ + private List getModifyFields(List oldFields, List newFields) { + Map oldFieldMap = oldFields.stream() + .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); + + return newFields.stream() + .filter(f -> { + TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); + return oldField != null && ( + !oldField.getDataType().equals(f.getDataType()) || + !Objects.equals(oldField.getDataLength(), f.getDataLength()) + ); + }) + .collect(Collectors.toList()); + } + + /** + * 获取需要删除的字段 + */ + private List getDropFields(List oldFields, List newFields) { + Set newFieldNames = newFields.stream() + .map(TdFieldDO::getFieldName) + .collect(Collectors.toSet()); + return oldFields.stream() + .filter(f -> !"time".equals(f.getFieldName())) + .filter(f -> !newFieldNames.contains(f.getFieldName())) + .collect(Collectors.toList()); + } + + /** + * 构建物模型 + */ private ThingModelRespVO buildThingModel(IotProductDO product, List functionList) { ThingModelRespVO thingModel = new ThingModelRespVO(); thingModel.setId(product.getId()); thingModel.setProductKey(product.getProductKey()); - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); List properties = functionList.stream() .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals( IotProductFunctionTypeEnum.valueOfType(function.getType()))) .map(this::buildThingModelProperty) .collect(Collectors.toList()); + ThingModelRespVO.Model model = new ThingModelRespVO.Model(); model.setProperties(properties); thingModel.setModel(model); return thingModel; } + /** + * 构建物模型属性 + */ private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) { ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class); property.setDataType(function.getProperty().getDataType()); return property; } + /** + * 获取数据库名称 + */ private String getDatabaseName() { - return url.substring(url.lastIndexOf("/") + 1); + int index = url.lastIndexOf("/"); + return index != -1 ? url.substring(index + 1) : url; } - static String getProductPropertySTableName(Integer deviceType, String productKey) { - return switch (deviceType) { - case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); - case 2 -> String.format("gateway_%s", productKey).toLowerCase(); - default -> String.format("device_%s", productKey).toLowerCase(); + /** + * 获取超级表名称 + */ + private String getSuperTableName(Integer deviceType, String productKey) { + String prefix = switch (deviceType) { + case 1 -> "gateway_sub_"; + case 2 -> "gateway_"; + default -> "device_"; }; + return (prefix + productKey).toLowerCase(); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java deleted file mode 100644 index 753284acae..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineService.java +++ /dev/null @@ -1,142 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.domain.SelectDto; -import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; -import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; - -import java.util.List; -import java.util.Map; - -/** - * TdEngineService - */ -public interface IotTdEngineService { - - /** - * 创建数据库 - * - * @param dataBaseName 数据库名称 - * @throws Exception 异常 - */ - void createDateBase(String dataBaseName) throws Exception; - - /** - * 创建超级表 - * - * @param schemaFields schema字段 - * @param tagsFields tags字段 - * @param superTableName 超级表名称 - */ - void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName); - - /** - * 检查超级表是否存在 - */ - Integer checkSuperTableExists(String dataBaseName, String superTableName); - - - /** - * 获取超级表的结构信息 - */ - List> describeSuperTable(String dataBaseName, String superTableName); - - /** - * 为超级表添加列 - * - * @param dataBaseName 数据库名称 - * @param superTableName 超级表名称 - * @param fieldsVo 字段信息 - */ - void addColumnForSuperTable(String dataBaseName, String superTableName, List fieldsVo); - - /** - * 为超级表删除列 - * - * @param dataBaseName 数据库名称 - * @param superTableName 超级表名称 - * @param fieldsVo 字段信息 - */ - void dropColumnForSuperTable(String dataBaseName, String superTableName, List fieldsVo); - - /** - * 为超级表添加tag - */ - Long getCountByTimesTamp(SelectDto selectDto) throws Exception; - - - /** - * 创建表 - * - * @param tableDto 表信息 - */ - void createTable(TableDO tableDto); - - /** - * 插入数据 - * - * @param tableDto 表信息 - */ - void insertData(TableDO tableDto); - - /** - * 根据时间戳查询数据 - * - * @param selectDto 查询条件 - * @return 数据 - * @throws Exception 异常 - */ - List> selectByTimesTamp(SelectDto selectDto) throws Exception; - - /** - * 初始化超级表 - * - * @param msg 消息 - * @throws Exception 异常 - */ - void initSTableFrame(String msg) throws Exception; - - /** - * 获取最新数据 - * - * @param selectDto 查询条件 - * @return 数据 - * @throws Exception 异常 - */ - Map getLastData(SelectDto selectDto) throws Exception; - - /** - * 根据tag查询最新数据 - * - * @param tagsSelectDao 查询条件 - * @return 数据 - */ - Map> getLastDataByTags(TagsSelectDao tagsSelectDao); - - /** - * 获取历史数据 - * - * @param selectVisualDto 查询条件 - * @return 数据 - */ - List> getHistoryData(SelectVisualDto selectVisualDto); - - /** - * 获取实时数据 - * - * @param selectVisualDto 查询条件 - * @return 数据 - */ - List> getRealtimeData(SelectVisualDto selectVisualDto); - - /** - * 获取聚合数据 - * - * @param selectVisualDto 查询条件 - * @return 数据 - */ - List> getAggregateData(SelectVisualDto selectVisualDto); - - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java deleted file mode 100644 index c7f7e73093..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotTdEngineServiceImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineMapper; -import cn.iocoder.yudao.module.iot.domain.SelectDto; -import cn.iocoder.yudao.module.iot.domain.TagsSelectDao; -import cn.iocoder.yudao.module.iot.domain.visual.SelectVisualDto; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -@Service -@Slf4j -public class IotTdEngineServiceImpl implements IotTdEngineService { - - @Resource - private TdEngineMapper tdEngineMapper; - - @Override - public void createDateBase(String dataBaseName) { - tdEngineMapper.createDatabase(dataBaseName); - } - - @Override - public void createSuperTable(List schemaFields, List tagsFields, String dataBaseName, String superTableName) { - tdEngineMapper.createSuperTable(schemaFields, tagsFields, dataBaseName, superTableName); - } - - @Override - public void createTable(TableDO tableDto) { - tdEngineMapper.createTable(tableDto); - } - - @Override - public void insertData(TableDO tableDto) { - tdEngineMapper.insertData(tableDto); - } - - @Override - public List> selectByTimesTamp(SelectDto selectDto) { - return List.of(); - } - - @Override - public void addColumnForSuperTable(String dataBaseName,String superTableName, List fields) { - for (TdFieldDO field : fields) { - tdEngineMapper.addColumnForSuperTable(dataBaseName,superTableName, field); - } - } - - @Override - public void dropColumnForSuperTable(String dataBaseName,String superTableName, List fields) { - for (TdFieldDO field : fields) { - tdEngineMapper.dropColumnForSuperTable(dataBaseName,superTableName, field); - } - } - - @Override - public Long getCountByTimesTamp(SelectDto selectDto) { - return 0L; - } - - @Override - public void initSTableFrame(String msg) { - - } - - @Override - public Map getLastData(SelectDto selectDto) { - return Map.of(); - } - - @Override - public Map> getLastDataByTags(TagsSelectDao tagsSelectDao) { - return Map.of(); - } - - @Override - public List> getHistoryData(SelectVisualDto selectVisualDto) { - return List.of(); - } - - @Override - public List> getRealtimeData(SelectVisualDto selectVisualDto) { - return List.of(); - } - - @Override - public List> getAggregateData(SelectVisualDto selectVisualDto) { - return List.of(); - } - - @Override - public Integer checkSuperTableExists(String dataBaseName, String superTableName) { - List> results = tdEngineMapper.showSuperTables(dataBaseName, superTableName); - return results == null || results.isEmpty() ? 0 : results.size(); - } - - @Override - public List> describeSuperTable(String dataBaseName, String superTableName) { - return tdEngineMapper.describeSuperTable(dataBaseName, superTableName); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java index b2f51a7f83..ffcb3063c5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -11,8 +11,8 @@ public interface IotThingModelMessageService { /** * 保存物模型消息 * - * @param device 设备 + * @param device 设备 * @param thingModelMessage 物模型消息 */ - void saveThingModelMessage(IotDeviceDO device,ThingModelMessage thingModelMessage); + void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 9bad7613ed..69c4178398 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -6,8 +6,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSt import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; @@ -23,6 +23,9 @@ import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; +/** + * 物模型消息 Service 实现类 + */ @Slf4j @Service public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { @@ -35,7 +38,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private IotDeviceService iotDeviceService; @Resource - private IotTdEngineService iotTdEngineService; + private TdEngineTableService tdEngineTableService; + @Resource + private TdEngineSuperTableService tdEngineSuperTableService; + @Resource + private TdEngineDataWriterService tdEngineDataWriterService; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @@ -90,14 +97,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ return; } - // 构建并保存设备属性 - TableDO tableData = new TableDO(); - tableData.setDataBaseName(getDatabaseName()); - tableData.setSuperTableName(getProductPropertySTableName(device.getDeviceType(), device.getProductKey())); - tableData.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); - tableData.setSchemaFieldValues(schemaFieldValues); - - iotTdEngineService.insertData(tableData); + // 构建并保存设备属性数据 + tdEngineDataWriterService.insertData(TdTableDO.builder().build() + .setDataBaseName(getDatabaseName()) + .setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) + .setColumns(schemaFieldValues)); } /** @@ -135,7 +139,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ String superTableName = getProductPropertySTableName(deviceType, productKey); String dataBaseName = getDatabaseName(); - List> maps = iotTdEngineService.describeSuperTable(dataBaseName, superTableName); + List> maps = tdEngineSuperTableService.describeSuperTable(new TdTableDO(dataBaseName, superTableName)); List tagsFieldValues = new ArrayList<>(); if (maps != null) { @@ -159,13 +163,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 创建设备数据表 String tableName = getDeviceTableName(productKey, deviceName); - TableDO tableDto = new TableDO(); - tableDto.setDataBaseName(dataBaseName); - tableDto.setSuperTableName(superTableName); - tableDto.setTableName(tableName); - tableDto.setTagsFieldValues(tagsFieldValues); - - iotTdEngineService.createTable(tableDto); + tdEngineTableService.createTable(TdTableDO.builder().build() + .setDataBaseName(dataBaseName) + .setSuperTableName(superTableName) + .setTableName(tableName) + .setTags(tagsFieldValues)); } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java new file mode 100644 index 0000000000..0995b07977 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; + +/** + * TD 引擎的数据写入 Service 接口 + */ +public interface TdEngineDataWriterService { + + /** + * 插入数据 - 指定列插入数据 + * + * @param table 数据 + * dataBaseName 数据库名 + * tableName 表名 + * columns 列 + */ + void insertData(TdTableDO table); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java new file mode 100644 index 0000000000..f4dd2c8fd7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +/** + * TD 引擎的数据写入 Service 实现类 + */ +@Service +public class TdEngineDataWriterServiceImpl implements TdEngineDataWriterService { + + @Resource + private TdEngineDataWriterMapper tdEngineDataWriterMapper; + + @Override + public void insertData(TdTableDO table) { + tdEngineDataWriterMapper.insertData(table); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java new file mode 100644 index 0000000000..d895262cca --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +/** + * TD 引擎的数据库 Service 接口 + */ +public interface TdEngineDatabaseService { + + /** + * 创建数据库 + * + * @param dataBaseName 数据库名称 + */ + void createDatabase(String dataBaseName); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java new file mode 100644 index 0000000000..c880db8a93 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * TD 引擎的数据库 Service 实现类 + */ +@Service +@Slf4j +public class TdEngineDatabaseServiceImpl implements TdEngineDatabaseService { + + @Resource + private TdEngineDatabaseMapper tdEngineDatabaseMapper; + + @Override + public void createDatabase(String dataBaseName) { + tdEngineDatabaseMapper.createDatabase(dataBaseName); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java new file mode 100644 index 0000000000..8d07b43344 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; + +import java.util.List; +import java.util.Map; + +/** + * TD 引擎的查询 Service 接口 + */ +public interface TdEngineQueryService { + + /** + * 获取历史数据 + * + * @param selectVisualDO 查询条件 + * @return 历史数据列表 + */ + List> getHistoryData(SelectVisualDO selectVisualDO); + + /** + * 获取历史数据条数 + * + * @param selectVisualDO 查询条件 + * @return 数据条数 + */ + Long getHistoryCount(SelectVisualDO selectVisualDO); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java new file mode 100644 index 0000000000..672a58640a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineQueryMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +@Service +public class TdEngineQueryServiceImpl implements TdEngineQueryService { + + @Resource + private TdEngineQueryMapper tdEngineQueryMapper; + + @Override + public List> getHistoryData(SelectVisualDO selectVisualDO) { + return tdEngineQueryMapper.getHistoryData(selectVisualDO); + } + + @Override + public Long getHistoryCount(SelectVisualDO selectVisualDO) { + return tdEngineQueryMapper.getHistoryCount(selectVisualDO); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java new file mode 100644 index 0000000000..df6290a983 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java @@ -0,0 +1,123 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; + +import java.util.List; +import java.util.Map; + +/** + * TD 引擎的超级表 Service 接口 + */ +public interface TdEngineSuperTableService { + + /** + * 创建超级表 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * columns 列信息 + * tags 标签信息 + */ + void createSuperTable(TdTableDO superTable); + + /** + * 查看超级表 - 显示当前数据库下的所有超级表信息 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + */ + + List> showSuperTables(TdTableDO superTable); + + /** + * 查看超级表 - 获取超级表的结构信息 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + */ + List> describeSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 增加列 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * column 列信息 + */ + void addColumnForSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 删除列 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * column 列信息 + */ + void dropColumnForSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 修改列宽 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * column 列信息 + */ + void modifyColumnWidthForSuperTable(TdTableDO superTable); + + + /** + * 修改超级表 - 为超级表添加标签 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tag 标签信息 + */ + void addTagForSuperTable(TdTableDO superTable); + + /** + * 修改超级表 - 为超级表删除标签 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tag 标签信息 + */ + void dropTagForSuperTable(TdTableDO superTable); + + /** + * 检查超级表是否存在 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * @return 超级表数量 + */ + Integer checkSuperTableExists(TdTableDO superTable); + + /** + * 为超级表添加列 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * columns 列信息 + */ + void addColumnsForSuperTable(TdTableDO superTable); + + /** + * 为超级表删除列 + * + * @param superTable 超级表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * columns 列信息 + */ + void dropColumnsForSuperTable(TdTableDO superTable); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java new file mode 100644 index 0000000000..60436e7bd1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineSuperTableMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * TD 引擎的超级表 Service 实现类 + */ +@Slf4j +@Service +public class TdEngineSuperTableServiceImpl implements TdEngineSuperTableService { + + @Resource + private TdEngineSuperTableMapper tdEngineSuperTableMapper; + + @Override + public void createSuperTable(TdTableDO superTable) { + tdEngineSuperTableMapper.createSuperTable(superTable); + } + + @Override + public List> showSuperTables(TdTableDO superTable) { + return tdEngineSuperTableMapper.showSuperTables(superTable); + } + + @Override + public List> describeSuperTable(TdTableDO superTable) { + return tdEngineSuperTableMapper.describeSuperTable(superTable); + } + + @Override + public void addColumnForSuperTable(TdTableDO superTable) { + tdEngineSuperTableMapper.addColumnForSuperTable(superTable); + } + + @Override + public void dropColumnForSuperTable(TdTableDO superTable) { + tdEngineSuperTableMapper.dropColumnForSuperTable(superTable); + } + + @Override + public void modifyColumnWidthForSuperTable(TdTableDO superTable) { + tdEngineSuperTableMapper.modifyColumnWidthForSuperTable(superTable); + } + + @Override + public void addTagForSuperTable(TdTableDO superTable) { + tdEngineSuperTableMapper.addTagForSuperTable(superTable); + } + + @Override + public void dropTagForSuperTable(TdTableDO superTable) { + tdEngineSuperTableMapper.dropTagForSuperTable(superTable); + } + + @Override + public Integer checkSuperTableExists(TdTableDO superTable) { + List> results = tdEngineSuperTableMapper.showSuperTables(superTable); + return results == null || results.isEmpty() ? 0 : results.size(); + } + + @Override + public void addColumnsForSuperTable(TdTableDO superTable) { + for (TdFieldDO column : superTable.getColumns()) { + tdEngineSuperTableMapper.addColumnForSuperTable(TdTableDO.builder() + .dataBaseName(superTable.getDataBaseName()) + .superTableName(superTable.getSuperTableName()) + .column(column) + .build()); + } + } + + @Override + public void dropColumnsForSuperTable(TdTableDO superTable) { + for (TdFieldDO column : superTable.getColumns()) { + tdEngineSuperTableMapper.dropColumnForSuperTable(TdTableDO.builder() + .dataBaseName(superTable.getDataBaseName()) + .superTableName(superTable.getSuperTableName()) + .column(column) + .build()); + } + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java new file mode 100644 index 0000000000..0781597848 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; + +/** + * TD 引擎的表 Service 接口 + */ +public interface TdEngineTableService { + + /** + * 创建表 - 创建超级表的子表 + * + * @param table 表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tableName 子表名称 + * tags TAG 字段 + */ + void createTable(TdTableDO table); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java new file mode 100644 index 0000000000..cdca47888a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.service.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +/** + * TD 引擎的表 Service 实现类 + */ +@Slf4j +@Service +public class TdEngineTableServiceImpl implements TdEngineTableService { + + @Resource + private TdEngineTableMapper tdEngineTableMapper; + + @Override + public void createTable(TdTableDO table) { + tdEngineTableMapper.createTable(table); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java index a6bf7b4583..f60610faff 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java @@ -75,5 +75,5 @@ public interface IotThinkModelFunctionService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getThinkModelFunctionListByProductKey(String productKey); + List getThinkModelFunctionListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 25cfe7b462..404a2aaa05 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -22,7 +22,7 @@ import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotDbStructureDataService; +import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -52,7 +52,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe @Resource private IotProductService productService; @Resource - private IotDbStructureDataService dbStructureDataService; + private IotSuperTableService dbStructureDataService; @Override @Transactional(rollbackFor = Exception.class) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml new file mode 100644 index 0000000000..b5f0446149 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + INSERT INTO ${dataBaseName}.${tableName} + + ${item.fieldName} + + VALUES + + #{item.fieldValue} + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml new file mode 100644 index 0000000000..4d9a125db6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + CREATE DATABASE IF NOT EXISTS ${dataBaseName} + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml deleted file mode 100644 index 4e127e44b1..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineMapper.xml +++ /dev/null @@ -1,327 +0,0 @@ - - - - - - - - CREATE DATABASE IF NOT EXISTS ${dataBaseName} - - - - CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName} - - - ${item.fieldName} - - - - - TIMESTAMP - - - TINYINT - - - SMALLINT - - - INT - - - BIGINT - - - FLOAT - - - DOUBLE - - - BINARY - - - NCHAR - - - BOOL - - - JSON - - - - - ( - ${item.dataLength} - ) - - - TAGS - - - - ${item.fieldName} - - - - - TIMESTAMP - - - TINYINT - - - SMALLINT - - - INT - - - BIGINT - - - FLOAT - - - DOUBLE - - - BINARY - - - NCHAR - - - BOOL - - - JSON - - - - - ( - ${item.dataLength} - ) - - - - - - - CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} - USING ${dataBaseName}.${superTableName} - - ${item.fieldName} - - TAGS - - #{item.fieldValue} - - - - - - INSERT INTO ${dataBaseName}.${tableName} - - ${item.fieldName} - - VALUES - - #{item.fieldValue} - - - - - - - ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN - - #{field.fieldName} - - - - - TIMESTAMP - - - TINYINT - - - SMALLINT - - - INT - - - BIGINT - - - FLOAT - - - DOUBLE - - - BINARY - - - NCHAR - - - BOOL - - - JSON - - - - - ( - #{field.dataLength} - ) - - - - - ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN - - #{field.fieldName} - - - - - ALTER - STABLE - #{superTableName} - ADD - TAG - - #{fieldDO.fieldName} - - - - - timestamp - - - tinyint - - - smallint - - - int - - - bigint - - - float - - - double - - - binary - - - nchar - - - bool - - - json - - - - - ( - #{fieldDO.dataLength} - ) - - - - - ALTER - STABLE - #{superTableName} - DROP - TAG - - #{fieldsVo.fieldName} - - - - - - - - - - - - - - - - - - - - - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml new file mode 100644 index 0000000000..9407b371d9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml new file mode 100644 index 0000000000..1ddbe3b977 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml @@ -0,0 +1,72 @@ + + + + + + + + CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName} + + ${item.fieldName} ${item.dataType} + + (${item.dataLength}) + + + TAGS + + ${item.fieldName} ${item.dataType} + + (${item.dataLength}) + + + + + + + + + + + + + ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN ${column.fieldName} ${column.dataType} + + (${column.dataLength}) + + + + + + ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN ${column.fieldName} + + + + + ALTER STABLE ${dataBaseName}.${superTableName} MODIFY COLUMN ${column.fieldName} ${column.dataType} + + (${column.dataLength}) + + + + + + ALTER STABLE ${dataBaseName}.${superTableName} ADD TAG ${tag.fieldName} ${tag.dataType} + + (${tag.dataLength}) + + + + + + ALTER STABLE ${dataBaseName}.${superTableName} DROP TAG ${tag.fieldName} + + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml new file mode 100644 index 0000000000..2d469a3905 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName} + TAGS + + #{item.fieldValue} + + + + + + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName} + + #{item.fieldName} + + TAGS + + #{item.fieldValue} + + + + \ No newline at end of file From 9b30d5d35572dc54646b2c3f3ad2fe04368fcd5d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 9 Nov 2024 13:39:51 +0800 Subject: [PATCH 011/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9Atdengine=20=E5=B0=81=E8=A3=85?= =?UTF-8?q?=E7=9A=84=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceDataController.java | 2 ++ .../vo/deviceData/IotDeviceDataReqVO.java | 1 + .../dataobject/device/IotDeviceDataDO.java | 1 + .../iot/dal/dataobject/tdengine/SelectDO.java | 4 ++++ .../dataobject/tdengine/SelectVisualDO.java | 1 + .../dal/dataobject/tdengine/TableManager.java | 1 + .../dal/dataobject/tdengine/TdRestApi.java | 1 + .../dal/dataobject/tdengine/TdTableDO.java | 1 + .../iot/dal/mysql/device/IotDeviceMapper.java | 1 + .../IotThinkModelFunctionMapper.java | 5 +++-- .../dal/tdengine/TdEngineDatabaseMapper.java | 1 + .../iot/dal/tdengine/TdEngineQueryMapper.java | 1 + .../iot/emq/service/EmqxServiceImpl.java | 2 ++ .../device/IotDeviceDataServiceImpl.java | 1 + .../service/device/IotDeviceServiceImpl.java | 21 +++++++++---------- .../IotThingModelMessageServiceImpl.java | 13 +++++++++++- .../tdengine/TdEngineDataWriterMapper.xml | 1 - .../tdengine/TdEngineDatabaseMapper.xml | 1 - .../mapper/tdengine/TdEngineQueryMapper.xml | 1 - .../tdengine/TdEngineSuperTableMapper.xml | 1 - .../mapper/tdengine/TdEngineTableMapper.xml | 1 - 21 files changed, 43 insertions(+), 19 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index e710d0ea8f..ac2ec5cf78 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -31,6 +31,7 @@ public class IotDeviceDataController { @Resource private IotDeviceDataService deviceDataService; + // TODO @haohao:是不是叫 get-latest 就好了。因为 data 已经在 url 里了哈 @GetMapping("/latest-data") @Operation(summary = "获取设备属性最新数据") public CommonResult> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) { @@ -38,6 +39,7 @@ public class IotDeviceDataController { return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); } + // TODO @haohao:是不是叫 /history-data => page @GetMapping("/history-data") @Operation(summary = "获取设备属性历史数据") public CommonResult> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java index 5f87a1cb82..21e53aff81 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java @@ -10,6 +10,7 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +// TODO @haohao:IotDeviceDataPageReqVO @Schema(description = "管理后台 - IoT 设备数据 Request VO") @Data public class IotDeviceDataReqVO extends PageParam { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index 5d80511721..68370d7312 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -23,6 +23,7 @@ import java.time.LocalDateTime; @AllArgsConstructor public class IotDeviceDataDO { + // TODO @haohao:每个字段的关联关系,可以 @ 下哈。 /** * 设备编号 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java index 503b26310e..652c3aee6c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java @@ -3,12 +3,15 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import lombok.Data; import java.util.Set; + +// TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 /** * 查询DO */ @Data public class SelectDO { + // TODO @haoha:database 是个单词 /** * 数据库名称 */ @@ -39,6 +42,7 @@ public class SelectDO { */ private String type; + // TODO @haohao:这个字段,是啥哈? /** * 查询条件 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index d5faf45a9c..d4c35d66df 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java @@ -4,6 +4,7 @@ import lombok.Data; import java.util.Map; +// TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do @Data public class SelectVisualDO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java index 65663d8053..e11db58e7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import java.util.List; +// TODO @haohao:这个还有用哇? /** * TableManager 类用于管理 TDengine 表的创建、删除和结构信息获取 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index 6653ece40e..b2d1ac0292 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +// TODO @haohao:有部分非实体的部分,是不是可以搞到 iot 的 framework 包下,搞个 tdengine 包,作为框架级的封装,放在 dataobject,感觉不是很合理哈。【可以微信讨论下】 /** * TdRestApi 类用于处理 TDengine 的 REST API 请求 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java index 314c51de3c..3b30352e4c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java @@ -21,6 +21,7 @@ public class TdTableDO { */ private String dataBaseName; + // TODO @haohao:superTableName 和 tableName 是不是合并。因为每个 mapper 操作的时候,有且只会使用到其中一个。 /** * 超级表名称 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 3129f400c2..4d8315eb3c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -15,6 +15,7 @@ import org.apache.ibatis.annotations.Mapper; @Mapper public interface IotDeviceMapper extends BaseMapperX { + // TODO @haohao:可能多余的查询条件,要去掉哈 default PageResult selectPage(IotDevicePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java index 2883abe4e3..c8745aab69 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java @@ -43,7 +43,7 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX selectListByProductIdAndIdentifiersAndTypes(Long productId, List identifiers, - List types){ + List types) { return selectList(new LambdaQueryWrapperX() .eq(IotThinkModelFunctionDO::getProductId, productId) .in(IotThinkModelFunctionDO::getIdentifier, identifiers) @@ -55,7 +55,8 @@ public interface IotThinkModelFunctionMapper extends BaseMapperX selectListByProductKey(String productKey){ + default List selectListByProductKey(String productKey) { return selectList(IotThinkModelFunctionDO::getProductKey, productKey); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java index bc562ccca7..cd3eaa8b93 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +// TODO @haohao:InterceptorIgnore 忽略租户,可以在每个方法上,添加 @TenantIgnore 哈。 /** * TD 引擎的数据库 Mapper */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java index e5353f7ee9..a2408e3355 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java @@ -25,6 +25,7 @@ public interface TdEngineQueryMapper { */ List> selectByTimestamp(SelectDO selectDO); + // TODO @haohao:最好方法的命名,和数据库操作的保持一直。get => select。然后 selectList or selectOne /** * 根据时间戳获取数据条数 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index f338d62498..343f9ba21c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -31,6 +31,7 @@ public class EmqxServiceImpl implements EmqxService { // 根据不同的主题,处理不同的业务逻辑 if (topic.contains("/property/post")) { // 设备上报数据 topic /sys/f13f57c63e9/dianbiao1/thing/event/property/post + // TODO @hao:这块未来可能,搞个 IotTopicUrls 之类?把拼接和解析的逻辑,收敛 String productKey = topic.split("/")[2]; String deviceName = topic.split("/")[3]; String message = new String(mqttMessage.getPayload()); @@ -48,4 +49,5 @@ public class EmqxServiceImpl implements EmqxService { log.error("订阅默认主题失败", e); } } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 4179075dfb..8f68989c94 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -133,4 +133,5 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { private static String getDeviceTableName(String productKey, String deviceName) { return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 60743f6713..0ec737a30e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -213,31 +213,30 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Override public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { - // 校验存在 + // 1. 校验存在 + // TODO @haohao:这里的 iotDeviceDO => device。一个是去掉 iot,一个是去掉 DO 后缀。这样,简洁一点。 IotDeviceDO iotDeviceDO = validateDeviceExists(updateReqVO.getId()); - // 更新状态和更新时间 + // 2.1 更新状态和更新时间 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); - - // 以前是未激活,现在是上线,设置设备激活时间 + // TODO @haohao:下面几个状态的处理,可以考虑 if else if。这样,看起来会有层次感哈 + // 2.2.1 以前是未激活,现在是上线,设置设备激活时间 + // TODO @haohao:这里可以使用 ObjectUtils.equalsAny 类似这种哈。 if (Objects.equals(iotDeviceDO.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus()) && Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { updateObj.setActiveTime(LocalDateTime.now()); } - - // 如果是上线,设置上线时间 + // 2.2.2 如果是上线,设置上线时间 if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { updateObj.setLastOnlineTime(LocalDateTime.now()); } - - // 如果是离线,设置离线时间 + // 2.2.3 如果是离线,设置离线时间 if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) { updateObj.setLastOfflineTime(LocalDateTime.now()); } - - // 设置状态更新时间 + // 2.3 设置状态更新时间 updateObj.setStatusLastUpdateTime(LocalDateTime.now()); - + // 2.4 更新到数据库 deviceMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 69c4178398..280b32d9a4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -47,6 +47,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @TenantIgnore public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { @@ -55,12 +56,14 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 创建设备数据表 createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); // 更新设备状态 + // TODO @haohao:下面可以考虑,链式调用。iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO().setid().setstatus()) IotDeviceStatusUpdateReqVO updateReqVO = new IotDeviceStatusUpdateReqVO(); updateReqVO.setId(device.getId()); updateReqVO.setStatus(IotDeviceStatusEnum.ONLINE.getStatus()); iotDeviceService.updateDeviceStatus(updateReqVO); } + // TODO @haohao:这个变量,可以和 “过滤并收集有效的属性字段” 那块,因为关联度高一点。 // 获取设备属性 Map params = thingModelMessage.dataToMap(); @@ -70,12 +73,12 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .stream() .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType())) .toList(); - if (functionList.isEmpty()) { return; } // 获取属性标识符集合 + // TODO @haohao:这个变量,可以和 “过滤并收集有效的属性字段” 那块,因为关联度高一点。另外,可以使用 CollectionUtils。convertSet Set propertyIdentifiers = functionList.stream() .map(IotThinkModelFunctionDO::getIdentifier) .collect(Collectors.toSet()); @@ -90,9 +93,11 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ if (propertyIdentifiers.contains(key)) { schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); // 缓存设备属性 + // TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读 setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime()); } }); + // TODO @haohao:疑问,为什么 1 不继续哈? if (schemaFieldValues.size() == 1) { return; } @@ -127,6 +132,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ deviceDataRedisDAO.set(deviceData); } + // TODO @haohao:实现没问题哈。这个方法的空行有点多,逻辑分块上没这么明显。看看能不能改下。 /** * 创建设备数据表 * @@ -143,6 +149,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ List tagsFieldValues = new ArrayList<>(); if (maps != null) { + // TODO @haohao:一些字符串,是不是可以枚举起来哈。 + // TODO @haohao:这种过滤的,常用的,可以考虑用 CollectionUtils.filterList。一些常用的 stream 操作,适合封装哈 List> taggedNotesList = maps.stream() .filter(map -> "TAG".equals(map.get("note"))) .toList(); @@ -176,6 +184,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @return 数据库名称 */ private String getDatabaseName() { + // TODO @haohao:可以使用 StrUtil.subAftetLast 这种方法 return url.substring(url.lastIndexOf("/") + 1); } @@ -187,6 +196,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @return 产品属性表名 */ private static String getProductPropertySTableName(Integer deviceType, String productKey) { + // TODO @haohao:枚举下,会好点哈。 return switch (deviceType) { case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); case 2 -> String.format("gateway_%s", productKey).toLowerCase(); @@ -204,4 +214,5 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private static String getDeviceTableName(String productKey, String deviceName) { return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml index b5f0446149..dd38cd08ec 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml @@ -2,7 +2,6 @@ - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml index 4d9a125db6..919c301f73 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml @@ -2,7 +2,6 @@ - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml index 9407b371d9..f4361bc67f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml @@ -2,7 +2,6 @@ - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml index 1ddbe3b977..37cf719101 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml @@ -2,7 +2,6 @@ - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml index 2d469a3905..867ef8f437 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml @@ -2,7 +2,6 @@ - From 89fb71e8573d1f6552d70a699e3a8b1ec8612f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 9 Nov 2024 23:44:05 +0800 Subject: [PATCH 012/309] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91=20=E4=BC=98=E5=8C=96=20tdengine=20=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=95=B0=E6=8D=AE=E5=BA=93=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/enums/IotConstants.java | 38 +++++ .../admin/device/IotDeviceDataController.java | 16 +- ...ReqVO.java => IotDeviceDataPageReqVO.java} | 3 +- .../vo/deviceData/IotDeviceDataRespVO.java | 3 - .../dataobject/device/IotDeviceDataDO.java | 20 ++- .../iot/dal/dataobject/tdengine/SelectDO.java | 6 - .../dataobject/tdengine/SelectVisualDO.java | 3 + .../dal/dataobject/tdengine/TableManager.java | 159 ------------------ ...ableMapper.java => TdEngineDDLMapper.java} | 60 +++++-- .../iot/dal/tdengine/TdEngineDMLMapper.java | 103 ++++++++++++ .../tdengine/TdEngineDataWriterMapper.java | 25 --- .../dal/tdengine/TdEngineDatabaseMapper.java | 24 --- .../iot/dal/tdengine/TdEngineQueryMapper.java | 86 ---------- .../iot/dal/tdengine/TdEngineTableMapper.java | 41 ----- .../service/device/IotDeviceDataService.java | 6 +- .../device/IotDeviceDataServiceImpl.java | 22 +-- .../service/device/IotDeviceServiceImpl.java | 45 ++--- .../tdengine/IotSuperTableService.java | 2 +- .../tdengine/IotSuperTableServiceImpl.java | 59 ++++--- .../IotThingModelMessageServiceImpl.java | 130 +++++++------- .../tdengine/TdEngineDataWriterService.java | 19 --- .../TdEngineDataWriterServiceImpl.java | 21 --- .../tdengine/TdEngineDatabaseService.java | 14 -- .../tdengine/TdEngineDatabaseServiceImpl.java | 22 --- .../tdengine/TdEngineQueryService.java | 28 --- .../tdengine/TdEngineQueryServiceImpl.java | 26 --- .../tdengine/TdEngineSuperTableService.java | 123 -------------- .../TdEngineSuperTableServiceImpl.java | 90 ---------- .../tdengine/TdEngineTableService.java | 21 --- .../tdengine/TdEngineTableServiceImpl.java | 23 --- ...rTableMapper.xml => TdEngineDDLMapper.xml} | 34 +++- ...eQueryMapper.xml => TdEngineDMLMapper.xml} | 32 +++- .../tdengine/TdEngineDataWriterMapper.xml | 21 --- .../tdengine/TdEngineDatabaseMapper.xml | 12 -- .../mapper/tdengine/TdEngineTableMapper.xml | 32 ---- 35 files changed, 416 insertions(+), 953 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/{IotDeviceDataReqVO.java => IotDeviceDataPageReqVO.java} (92%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/{TdEngineSuperTableMapper.java => TdEngineDDLMapper.java} (68%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/{TdEngineSuperTableMapper.xml => TdEngineDDLMapper.xml} (70%) rename yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/{TdEngineQueryMapper.xml => TdEngineDMLMapper.xml} (68%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java new file mode 100644 index 0000000000..5927f44b96 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.enums; + +/** + * Iot 常量 + * + * @author 芋道源码 + */ +public interface IotConstants { + + /** + * 获取设备表名 + *

+ * 格式为 device_{productKey}_{deviceName} + */ + String DEVICE_TABLE_NAME_FORMAT = "device_%s_%s"; + + /** + * 获取产品属性超级表名 - 网关子设备 + *

+ * 格式为 gateway_sub_{productKey} + */ + String GATEWAY_SUB_STABLE_NAME_FORMAT = "gateway_sub_%s"; + + /** + * 获取产品属性超级表名 - 网关 + *

+ * 格式为 gateway_{productKey} + */ + String GATEWAY_STABLE_NAME_FORMAT = "gateway_%s"; + + /** + * 获取产品属性超级表名 - 设备 + *

+ * 格式为 device_{productKey} + */ + String DEVICE_STABLE_NAME_FORMAT = "device_%s"; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index ac2ec5cf78..72fdda8c83 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; @@ -31,19 +31,17 @@ public class IotDeviceDataController { @Resource private IotDeviceDataService deviceDataService; - // TODO @haohao:是不是叫 get-latest 就好了。因为 data 已经在 url 里了哈 - @GetMapping("/latest-data") + @GetMapping("/latest") @Operation(summary = "获取设备属性最新数据") - public CommonResult> getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) { - List list = deviceDataService.getDevicePropertiesLatestData(deviceDataReqVO); + public CommonResult> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { + List list = deviceDataService.getLatestDeviceProperties(deviceDataReqVO); return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); } - // TODO @haohao:是不是叫 /history-data => page - @GetMapping("/history-data") + @GetMapping("/history") @Operation(summary = "获取设备属性历史数据") - public CommonResult> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO) { - PageResult> list = deviceDataService.getDevicePropertiesHistoryData(deviceDataReqVO); + public CommonResult> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { + PageResult> list = deviceDataService.getHistoryDeviceProperties(deviceDataReqVO); return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataPageReqVO.java similarity index 92% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataPageReqVO.java index 21e53aff81..da41299d8e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataPageReqVO.java @@ -10,10 +10,9 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -// TODO @haohao:IotDeviceDataPageReqVO @Schema(description = "管理后台 - IoT 设备数据 Request VO") @Data -public class IotDeviceDataReqVO extends PageParam { +public class IotDeviceDataPageReqVO extends PageParam { @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") private Long deviceId; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java index 256bf84faa..ad32fb5f9d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java @@ -1,9 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Builder; import lombok.Data; import java.time.LocalDateTime; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index 68370d7312..892a6b8b76 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -1,15 +1,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import com.baomidou.mybatisplus.annotation.TableId; -import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.math.BigDecimal; import java.time.LocalDateTime; /** @@ -23,39 +20,52 @@ import java.time.LocalDateTime; @AllArgsConstructor public class IotDeviceDataDO { - // TODO @haohao:每个字段的关联关系,可以 @ 下哈。 /** * 设备编号 + *

+ * 关联 {@link IotDeviceDO#getId()} */ private Long deviceId; /** * 物模型编号 + *

+ * 关联 {@link IotThinkModelFunctionDO#getId()} */ private Long thinkModelFunctionId; /** * 产品标识 + *

+ * 关联 {@link IotProductDO#getProductKey()} */ private String productKey; /** * 设备名称 + *

+ * 冗余 {@link IotDeviceDO#getDeviceName()} */ private String deviceName; /** * 属性标识符 + *

+ * 关联 {@link IotThinkModelFunctionDO#getIdentifier()} */ private String identifier; /** * 属性名称 + *

+ * 关联 {@link IotThinkModelFunctionDO#getName()} */ private String name; /** * 数据类型 + *

+ * 关联 {@link IotThinkModelFunctionDO#getProperty()#getDataType()} */ private String dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java index 652c3aee6c..542dd1e7b7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java @@ -42,12 +42,6 @@ public class SelectDO { */ private String type; - // TODO @haohao:这个字段,是啥哈? - /** - * 查询条件 - */ - private Set orgIds; - /** * 设备ID */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index d4c35d66df..44acc3ac54 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java @@ -27,14 +27,17 @@ public class SelectVisualDO { * 查询类型,0历史数据,1实时数据,2聚合数据 */ private int type; + /** * 查询的数据量 */ private int num; + /** * 聚合函数 */ private String aggregate; + /** * 统计间隔数字+s/m/h/d * 比如1s,1m,1h,1d代表1秒,1分钟,1小时,1天 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java deleted file mode 100644 index e11db58e7d..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TableManager.java +++ /dev/null @@ -1,159 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import java.util.List; - -// TODO @haohao:这个还有用哇? -/** - * TableManager 类用于管理 TDengine 表的创建、删除和结构信息获取 - */ -public class TableManager { - - /** - * 创建超级表模板(含存在判断) - */ - private static final String CREATE_STABLE_INE_TPL = "CREATE STABLE IF NOT EXISTS %s (%s) TAGS (%s);"; - - /** - * 删除超级表 - */ - private static final String DROP_STABLE_TPL = "DROP STABLE IF EXISTS %s;"; - - /** - * 获取表的结构信息 - */ - private static final String DESC_TB_TPL = "DESCRIBE %s;"; - - /** - * 超级表增加列 - */ - private static final String ALTER_STABLE_ADD_COL_TPL = "ALTER STABLE %s ADD COLUMN %s;"; - - /** - * 超级表修改列 - */ - private static final String ALTER_STABLE_MODIFY_COL_TPL = "ALTER STABLE %s MODIFY COLUMN %s;"; - - /** - * 超级表删除列 - */ - private static final String ALTER_STABLE_DROP_COL_TPL = "ALTER STABLE %s DROP COLUMN %s;"; - - /** - * 创建普通表模板(含存在判断) - */ - private static final String CREATE_CTABLE_INE_TPL = "CREATE TABLE IF NOT EXISTS %s (%s)"; - - /** - * 获取创建表sql - */ - public static String getCreateSTableSql(String tbName, List fields, TdFieldDO... tags) { - if (fields.isEmpty()) { - return null; - } - - // 生成字段片段 - StringBuilder sbField = new StringBuilder("time TIMESTAMP,"); - - for (TdFieldDO field : fields) { - sbField.append(FieldParser.getFieldDefine(field)); - sbField.append(","); - } - sbField.deleteCharAt(sbField.length() - 1); - - String fieldFrag = sbField.toString(); - - // 生成tag - StringBuilder sbTag = new StringBuilder(); - for (TdFieldDO tag : tags) { - sbTag.append(FieldParser.getFieldDefine(tag)) - .append(","); - } - sbTag.deleteCharAt(sbTag.length() - 1); - - return String.format(CREATE_STABLE_INE_TPL, tbName, fieldFrag, sbTag); - - } - - /** - * 获取创建普通表sql - */ - public static String getCreateCTableSql(String tbName, List fields) { - if (fields.size() == 0) { - return null; - } - - //生成字段片段 - StringBuilder sbField = new StringBuilder("time timestamp,"); - - for (TdFieldDO field : fields) { - sbField.append(FieldParser.getFieldDefine(field)); - sbField.append(","); - } - sbField.deleteCharAt(sbField.length() - 1); - - String fieldFrag = sbField.toString(); - - return String.format(CREATE_CTABLE_INE_TPL, tbName, fieldFrag); - - } - - - /** - * 取正确的表名 - * - * @param name 表象 - */ - public static String rightTbName(String name) { - return name.toLowerCase().replace("-" , "_"); - } - - /** - * 获取表详情的sql - */ - public static String getDescTableSql(String tbName) { - return String.format(DESC_TB_TPL, tbName); - } - - /** - * 获取添加字段sql - */ - public static String getAddSTableColumnSql(String tbName, List fields) { - StringBuilder sbAdd = new StringBuilder(); - for (TdFieldDO field : fields) { - sbAdd.append(String.format(ALTER_STABLE_ADD_COL_TPL, - tbName, - FieldParser.getFieldDefine(field) - )); - } - return sbAdd.toString(); - } - - /** - * 获取修改字段sql - */ - public static String getModifySTableColumnSql(String tbName, List fields) { - StringBuilder sbModify = new StringBuilder(); - for (TdFieldDO field : fields) { - sbModify.append(String.format(ALTER_STABLE_MODIFY_COL_TPL, - tbName, - FieldParser.getFieldDefine(field) - )); - } - return sbModify.toString(); - } - - /** - * 获取删除字段sql - */ - public static String getDropSTableColumnSql(String tbName, List fields) { - StringBuilder sbDrop = new StringBuilder(); - for (TdFieldDO field : fields) { - sbDrop.append(String.format(ALTER_STABLE_DROP_COL_TPL, - tbName, - field.getFieldName() - )); - } - return sbDrop.toString(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java index e688563d5f..640255940d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineSuperTableMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java @@ -1,19 +1,30 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; /** - * TD 引擎的超级表 Mapper + * 专门处理 DDL(数据定义语言)操作,包含所有的数据库和表的定义操作,例如创建数据库、创建超级表、创建子表等 */ @Mapper @DS("tdengine") -public interface TdEngineSuperTableMapper { +public interface TdEngineDDLMapper { + + /** + * 创建数据库 + * SQL:CREATE DATABASE [IF NOT EXISTS] db_name [database_options]; + * + * @param dataBaseName 数据库名称 + */ + @TenantIgnore + void createDatabase(@Param("dataBaseName") String dataBaseName); /** * 创建超级表 @@ -25,7 +36,7 @@ public interface TdEngineSuperTableMapper { * columns 列信息 * tags 标签信息 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore void createSuperTable(TdTableDO superTable); /** @@ -36,7 +47,7 @@ public interface TdEngineSuperTableMapper { * dataBaseName 数据库名称 * superTableName 超级表名称 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore List> showSuperTables(TdTableDO superTable); /** @@ -47,7 +58,7 @@ public interface TdEngineSuperTableMapper { * dataBaseName 数据库名称 * superTableName 超级表名称 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore List> describeSuperTable(TdTableDO superTable); /** @@ -59,7 +70,7 @@ public interface TdEngineSuperTableMapper { * superTableName 超级表名称 * column 列信息 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore void addColumnForSuperTable(TdTableDO superTable); /** @@ -71,7 +82,7 @@ public interface TdEngineSuperTableMapper { * superTableName 超级表名称 * column 列信息 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore void dropColumnForSuperTable(TdTableDO superTable); /** @@ -83,7 +94,7 @@ public interface TdEngineSuperTableMapper { * superTableName 超级表名称 * column 列信息 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore void modifyColumnWidthForSuperTable(TdTableDO superTable); @@ -95,7 +106,7 @@ public interface TdEngineSuperTableMapper { * superTableName 超级表名称 * tag 标签信息 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore void addTagForSuperTable(TdTableDO superTable); /** @@ -106,6 +117,33 @@ public interface TdEngineSuperTableMapper { * superTableName 超级表名称 * tag 标签信息 */ - @InterceptorIgnore(tenantLine = "true") + @TenantIgnore void dropTagForSuperTable(TdTableDO superTable); -} \ No newline at end of file + + /** + * 创建子表 - 创建子表 + * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...); + * + * @param table 表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tableName 子表名称 + * tags TAG 字段 + */ + @TenantIgnore + void createTable(TdTableDO table); + + /** + * 创建子表 - 创建子表并指定标签的值 + * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...); + * + * @param table 表信息 + * dataBaseName 数据库名称 + * superTableName 超级表名称 + * tableName 子表名称 + * tags TAG 字段 + */ + @TenantIgnore + void createTableWithTags(TdTableDO table); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java new file mode 100644 index 0000000000..12b2c232c3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * 专门处理 TD Engine 的 DML(数据操作语言)操作,处理所有的数据查询和写入操作,如插入数据、查询数据等 + */ +@Mapper +@DS("tdengine") +public interface TdEngineDMLMapper { + + /** + * 插入数据 - 指定列插入数据 + * + * @param table 数据 + * dataBaseName 数据库名 + * tableName 表名 + * columns 列 + */ + @TenantIgnore + void insertData(TdTableDO table); + + /** + * 根据时间戳查询数据 + * + * @param selectDO 查询条件 + * @return 查询结果列表 + */ + @TenantIgnore + List> selectByTimestamp(SelectDO selectDO); + + /** + * 根据时间戳获取数据条数 + * + * @param selectDO 查询条件 + * @return 数据条数 + */ + @TenantIgnore + Map selectCountByTimestamp(SelectDO selectDO); + + /** + * 获取最新数据 + * + * @param selectDO 查询条件 + * @return 最新数据 + */ + @TenantIgnore + Map selectOneLastData(SelectDO selectDO); + + /** + * 获取历史数据列表 + * + * @param selectVisualDO 查询条件 + * @return 历史数据列表 + */ + @TenantIgnore + List> selectHistoryDataList(SelectVisualDO selectVisualDO); + + /** + * 获取实时数据列表 + * + * @param selectVisualDO 查询条件 + * @return 实时数据列表 + */ + @TenantIgnore + List> selectRealtimeDataList(SelectVisualDO selectVisualDO); + + /** + * 获取聚合数据列表 + * + * @param selectVisualDO 查询条件 + * @return 聚合数据列表 + */ + @TenantIgnore + List> selectAggregateDataList(SelectVisualDO selectVisualDO); + + /** + * 根据标签获取最新数据列表 + * + * @param tagsSelectDO 查询条件 + * @return 最新数据列表 + */ + @TenantIgnore + List> selectLastDataListByTags(TagsSelectDO tagsSelectDO); + + /** + * 获取历史数据条数 + * + * @param selectVisualDO 查询条件 + * @return 数据条数 + */ + @TenantIgnore + Long selectHistoryCount(SelectVisualDO selectVisualDO); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java deleted file mode 100644 index 51f4229a0f..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDataWriterMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import com.baomidou.dynamic.datasource.annotation.DS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Mapper; - -/** - * TD 引擎的数据写入 Mapper - */ -@Mapper -@DS("tdengine") -public interface TdEngineDataWriterMapper { - - /** - * 插入数据 - 指定列插入数据 - * - * @param table 数据 - * dataBaseName 数据库名 - * tableName 表名 - * columns 列 - */ - @InterceptorIgnore(tenantLine = "true") - void insertData(TdTableDO table); -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java deleted file mode 100644 index cd3eaa8b93..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDatabaseMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import com.baomidou.dynamic.datasource.annotation.DS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -// TODO @haohao:InterceptorIgnore 忽略租户,可以在每个方法上,添加 @TenantIgnore 哈。 -/** - * TD 引擎的数据库 Mapper - */ -@Mapper -@DS("tdengine") -public interface TdEngineDatabaseMapper { - - /** - * 创建数据库 - * SQL:CREATE DATABASE [IF NOT EXISTS] db_name [database_options]; - * - * @param dataBaseName 数据库名称 - */ - @InterceptorIgnore(tenantLine = "true") - void createDatabase(@Param("dataBaseName") String dataBaseName); -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java deleted file mode 100644 index a2408e3355..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineQueryMapper.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO; -import com.baomidou.dynamic.datasource.annotation.DS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; -import java.util.Map; - -/** - * TD 引擎的查询 Mapper - */ -@Mapper -@DS("tdengine") -public interface TdEngineQueryMapper { - - /** - * 根据时间戳查询数据 - * - * @param selectDO 查询条件 - * @return 查询结果 - */ - List> selectByTimestamp(SelectDO selectDO); - - // TODO @haohao:最好方法的命名,和数据库操作的保持一直。get => select。然后 selectList or selectOne - /** - * 根据时间戳获取数据条数 - * - * @param selectDO 查询条件 - * @return 数据条数 - */ - Map getCountByTimestamp(SelectDO selectDO); - - /** - * 获取最新数据 - * - * @param selectDO 查询条件 - * @return 最新数据 - */ - Map getLastData(SelectDO selectDO); - - /** - * 获取历史数据 - * - * @param selectVisualDO 查询条件 - * @return 历史数据列表 - */ - @InterceptorIgnore(tenantLine = "true") - List> getHistoryData(SelectVisualDO selectVisualDO); - - /** - * 获取实时数据 - * - * @param selectVisualDO 查询条件 - * @return 实时数据列表 - */ - List> getRealtimeData(SelectVisualDO selectVisualDO); - - /** - * 获取聚合数据 - * - * @param selectVisualDO 查询条件 - * @return 聚合数据列表 - */ - List> getAggregateData(SelectVisualDO selectVisualDO); - - /** - * 根据标签获取最新数据 - * - * @param tagsSelectDO 查询条件 - * @return 最新数据列表 - */ - List> getLastDataByTags(TagsSelectDO tagsSelectDO); - - /** - * 获取历史数据条数 - * - * @param selectVisualDO 查询条件 - * @return 数据条数 - */ - @InterceptorIgnore(tenantLine = "true") - Long getHistoryCount(SelectVisualDO selectVisualDO); -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java deleted file mode 100644 index 72517bcaba..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineTableMapper.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import com.baomidou.dynamic.datasource.annotation.DS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Mapper; - -/** - * TD 引擎的表 Mapper - */ -@Mapper -@DS("tdengine") -public interface TdEngineTableMapper { - - /** - * 创建子表 - 创建子表 - * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...); - * - * @param table 表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tableName 子表名称 - * tags TAG 字段 - */ - @InterceptorIgnore(tenantLine = "true") - void createTable(TdTableDO table); - - /** - * 创建子表 - 创建子表并指定标签的值 - * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...); - * - * @param table 表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tableName 子表名称 - * tags TAG 字段 - */ - @InterceptorIgnore(tenantLine = "true") - void createTableWithTags(TdTableDO table); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java index 96181fd263..4f390ca3be 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import jakarta.validation.Valid; @@ -31,7 +31,7 @@ public interface IotDeviceDataService { * @param deviceId 设备编号 * @return 设备属性最新数据 */ - List getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceId); + List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId); /** * 获得设备属性历史数据 @@ -39,5 +39,5 @@ public interface IotDeviceDataService { * @param deviceDataReqVO 设备属性历史数据 Request VO * @return 设备属性历史数据 */ - PageResult> getDevicePropertiesHistoryData(@Valid IotDeviceDataReqVO deviceDataReqVO); + PageResult> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 8f68989c94..7d10977f90 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -1,18 +1,20 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; +import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.tdengine.TdEngineQueryService; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -40,7 +42,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @Resource private IotThinkModelFunctionService thinkModelFunctionService; @Resource - private TdEngineQueryService tdEngineQueryService; + private TdEngineDMLMapper tdEngineDMLMapper; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @@ -66,7 +68,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { } @Override - public List getDevicePropertiesLatestData(@Valid IotDeviceDataReqVO deviceDataReqVO) { + public List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { List list = new ArrayList<>(); // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); @@ -106,7 +108,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { } @Override - public PageResult> getDevicePropertiesHistoryData(IotDeviceDataReqVO deviceDataReqVO) { + public PageResult> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) { PageResult> pageResult = new PageResult<>(); // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); @@ -121,17 +123,17 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { params.put("rows", deviceDataReqVO.getPageSize()); params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); selectVisualDO.setParams(params); - pageResult.setList(tdEngineQueryService.getHistoryData(selectVisualDO)); - pageResult.setTotal(tdEngineQueryService.getHistoryCount(selectVisualDO)); + pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO)); + pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO)); return pageResult; } private String getDatabaseName() { - return url.substring(url.lastIndexOf("/") + 1); + return StrUtil.subAfter(url, "/", true); } private static String getDeviceTableName(String productKey, String deviceName) { - return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); + return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 0ec737a30e..58918c1307 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -19,7 +19,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.security.SecureRandom; import java.time.LocalDateTime; +import java.util.Base64; import java.util.Objects; import java.util.UUID; @@ -137,10 +139,13 @@ public class IotDeviceServiceImpl implements IotDeviceService { * @return 生成的 MQTT Password */ private String generateMqttPassword() { - // TODO @haohao:【后续优化】在实际应用中,建议使用更安全的方法生成 MQTT Password,如加密或哈希 - return UUID.randomUUID().toString(); + SecureRandom secureRandom = new SecureRandom(); + byte[] passwordBytes = new byte[32]; // 256 位的随机数 + secureRandom.nextBytes(passwordBytes); + return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes); } + /** * 生成唯一的 DeviceName * @@ -214,30 +219,28 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Override public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { // 1. 校验存在 - // TODO @haohao:这里的 iotDeviceDO => device。一个是去掉 iot,一个是去掉 DO 后缀。这样,简洁一点。 - IotDeviceDO iotDeviceDO = validateDeviceExists(updateReqVO.getId()); + IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); // 2.1 更新状态和更新时间 - IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); - // TODO @haohao:下面几个状态的处理,可以考虑 if else if。这样,看起来会有层次感哈 - // 2.2.1 以前是未激活,现在是上线,设置设备激活时间 - // TODO @haohao:这里可以使用 ObjectUtils.equalsAny 类似这种哈。 - if (Objects.equals(iotDeviceDO.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus()) - && Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { - updateObj.setActiveTime(LocalDateTime.now()); - } - // 2.2.2 如果是上线,设置上线时间 - if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { - updateObj.setLastOnlineTime(LocalDateTime.now()); - } - // 2.2.3 如果是离线,设置离线时间 - if (Objects.equals(updateObj.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) { - updateObj.setLastOfflineTime(LocalDateTime.now()); + IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); + // 2.2 更新状态相关时间 + if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus()) + && Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { + // 从未激活到在线,设置激活时间和最后上线时间 + updateDevice.setActiveTime(LocalDateTime.now()); + updateDevice.setLastOnlineTime(LocalDateTime.now()); + } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { + // 如果是上线,设置最后上线时间 + updateDevice.setLastOnlineTime(LocalDateTime.now()); + } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) { + // 如果是离线,设置最后离线时间 + updateDevice.setLastOfflineTime(LocalDateTime.now()); } + // 2.3 设置状态更新时间 - updateObj.setStatusLastUpdateTime(LocalDateTime.now()); + updateDevice.setStatusLastUpdateTime(LocalDateTime.now()); // 2.4 更新到数据库 - deviceMapper.updateById(updateObj); + deviceMapper.updateById(updateDevice); } @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java index 7c88b83aa5..8aec32f323 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java @@ -15,4 +15,4 @@ public interface IotSuperTableService { * 创建超级表数据模型 */ void createSuperTableDataModel(IotProductDO product, List functionList); -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java index 3be324c4e6..20989181ea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java @@ -9,6 +9,8 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -26,7 +28,7 @@ import java.util.stream.Collectors; public class IotSuperTableServiceImpl implements IotSuperTableService { @Resource - private TdEngineSuperTableService tdEngineSuperTableService; + private TdEngineDDLMapper tdEngineDDLMapper; @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; @@ -42,9 +44,10 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey()); String databaseName = getDatabaseName(); - Integer tableExists = tdEngineSuperTableService.checkSuperTableExists(new TdTableDO(databaseName, superTableName)); - if (tableExists != null && tableExists > 0) { + List> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName)); + int tableExists = results == null || results.isEmpty() ? 0 : results.size(); + if (tableExists > 0) { updateSuperTable(thingModel, product.getDeviceType()); } else { createSuperTable(thingModel, product.getDeviceType()); @@ -76,7 +79,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { String databaseName = getDatabaseName(); // 创建超级表 - tdEngineSuperTableService.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); + tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); } /** @@ -98,7 +101,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { * 获取表的字段信息 */ private List getTableFields(String tableName) { - List> tableDescription = tdEngineSuperTableService.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); + List> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); if (CollUtil.isEmpty(tableDescription)) { return Collections.emptyList(); } @@ -127,32 +130,38 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { // 添加新增字段 if (CollUtil.isNotEmpty(addFields)) { - tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .columns(addFields) - .build()); + for (TdFieldDO addField : addFields) { + tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .column(addField) + .build()); + } } // 删除旧字段 if (CollUtil.isNotEmpty(dropFields)) { - tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .columns(dropFields) - .build()); + for (TdFieldDO dropField : dropFields) { + tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .column(dropField) + .build()); + } } // 修改字段(先删除再添加) if (CollUtil.isNotEmpty(modifyFields)) { - tdEngineSuperTableService.dropColumnsForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .columns(modifyFields) - .build()); - tdEngineSuperTableService.addColumnsForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .columns(addFields) - .build()); + for (TdFieldDO modifyField : modifyFields) { + tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .column(modifyField) + .build()); + tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() + .dataBaseName(databaseName) + .superTableName(tableName) + .column(modifyField) + .build()); + } } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 280b32d9a4..92a30ebb0d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -11,6 +13,9 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; +import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; @@ -30,6 +35,14 @@ import java.util.stream.Collectors; @Service public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { + private static final String TAG_NOTE = "TAG"; + private static final String NOTE = "note"; + private static final String TIME = "time"; + private static final String DEVICE_KEY = "device_key"; + private static final String DEVICE_NAME = "device_name"; + private static final String PRODUCT_KEY = "product_key"; + private static final String DEVICE_TYPE = "device_type"; + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; @@ -38,11 +51,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private IotDeviceService iotDeviceService; @Resource - private TdEngineTableService tdEngineTableService; + private TdEngineDDLMapper tdEngineDDLMapper; @Resource - private TdEngineSuperTableService tdEngineSuperTableService; - @Resource - private TdEngineDataWriterService tdEngineDataWriterService; + private TdEngineDMLMapper tdEngineDMLMapper; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @@ -51,62 +62,64 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Override @TenantIgnore public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { - // 判断设备状态,如果为未激活状态,创建数据表 + // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { - // 创建设备数据表 createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); - // 更新设备状态 - // TODO @haohao:下面可以考虑,链式调用。iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO().setid().setstatus()) - IotDeviceStatusUpdateReqVO updateReqVO = new IotDeviceStatusUpdateReqVO(); - updateReqVO.setId(device.getId()); - updateReqVO.setStatus(IotDeviceStatusEnum.ONLINE.getStatus()); - iotDeviceService.updateDeviceStatus(updateReqVO); + iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() + .setId(device.getId()) + .setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); } - // TODO @haohao:这个变量,可以和 “过滤并收集有效的属性字段” 那块,因为关联度高一点。 - // 获取设备属性 + // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 Map params = thingModelMessage.dataToMap(); - - // 物模型校验,过滤非物模型属性 - List functionList = iotThinkModelFunctionService - .getThinkModelFunctionListByProductKey(thingModelMessage.getProductKey()) - .stream() - .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType())) - .toList(); + List functionList = getValidFunctionList(thingModelMessage.getProductKey()); if (functionList.isEmpty()) { return; } - // 获取属性标识符集合 - // TODO @haohao:这个变量,可以和 “过滤并收集有效的属性字段” 那块,因为关联度高一点。另外,可以使用 CollectionUtils。convertSet - Set propertyIdentifiers = functionList.stream() - .map(IotThinkModelFunctionDO::getIdentifier) - .collect(Collectors.toSet()); + // 3. 过滤并收集有效的属性字段,缓存设备属性 + List schemaFieldValues = filterAndCollectValidFields(params, functionList, device, thingModelMessage.getTime()); + if (schemaFieldValues.size() == 1) { // 仅有时间字段,无需保存 + return; + } + + // 4. 构建并保存设备属性数据 + tdEngineDMLMapper.insertData(TdTableDO.builder() + .dataBaseName(getDatabaseName()) + .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) + .columns(schemaFieldValues) + .build()); + } + + private List getValidFunctionList(String productKey) { + return iotThinkModelFunctionService + .getThinkModelFunctionListByProductKey(productKey) + .stream() + .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType())) + .toList(); + } + + private List filterAndCollectValidFields(Map params, List functionList, IotDeviceDO device, Long time) { + // 1. 获取属性标识符集合 + Set propertyIdentifiers = CollectionUtils.convertSet(functionList, IotThinkModelFunctionDO::getIdentifier); + + // 2. 构建属性标识符和属性的映射 Map functionMap = functionList.stream() .collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, function -> function)); - // 过滤并收集有效的属性字段 + // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); - schemaFieldValues.add(new TdFieldDO("time", thingModelMessage.getTime())); + schemaFieldValues.add(new TdFieldDO(TIME, time)); params.forEach((key, val) -> { if (propertyIdentifiers.contains(key)) { schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); // 缓存设备属性 // TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读 - setDeviceDataCache(device, functionMap.get(key), val, thingModelMessage.getTime()); + setDeviceDataCache(device, functionMap.get(key), val, time); } }); - // TODO @haohao:疑问,为什么 1 不继续哈? - if (schemaFieldValues.size() == 1) { - return; - } - - // 构建并保存设备属性数据 - tdEngineDataWriterService.insertData(TdTableDO.builder().build() - .setDataBaseName(getDatabaseName()) - .setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) - .setColumns(schemaFieldValues)); + return schemaFieldValues; } /** @@ -132,7 +145,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ deviceDataRedisDAO.set(deviceData); } - // TODO @haohao:实现没问题哈。这个方法的空行有点多,逻辑分块上没这么明显。看看能不能改下。 /** * 创建设备数据表 * @@ -142,36 +154,37 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @param deviceKey 设备 Key */ private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) { + // 1. 获取超级表名和数据库名 String superTableName = getProductPropertySTableName(deviceType, productKey); String dataBaseName = getDatabaseName(); - List> maps = tdEngineSuperTableService.describeSuperTable(new TdTableDO(dataBaseName, superTableName)); + // 2. 获取超级表的结构信息 + List> maps = tdEngineDDLMapper.describeSuperTable(new TdTableDO(dataBaseName, superTableName)); List tagsFieldValues = new ArrayList<>(); - if (maps != null) { - // TODO @haohao:一些字符串,是不是可以枚举起来哈。 - // TODO @haohao:这种过滤的,常用的,可以考虑用 CollectionUtils.filterList。一些常用的 stream 操作,适合封装哈 - List> taggedNotesList = maps.stream() - .filter(map -> "TAG".equals(map.get("note"))) - .toList(); + // 2.1 过滤出 TAG 类型的字段 + List> taggedNotesList = CollectionUtils.filterList(maps, map -> TAG_NOTE.equals(map.get(NOTE))); + + // 2.2 解析字段信息 tagsFieldValues = FieldParser.parse(taggedNotesList.stream() .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) .collect(Collectors.toList())); + // 2.3 设置 TAG 字段的值 for (TdFieldDO tagsFieldValue : tagsFieldValues) { switch (tagsFieldValue.getFieldName()) { - case "product_key" -> tagsFieldValue.setFieldValue(productKey); - case "device_key" -> tagsFieldValue.setFieldValue(deviceKey); - case "device_name" -> tagsFieldValue.setFieldValue(deviceName); - case "device_type" -> tagsFieldValue.setFieldValue(deviceType); + case PRODUCT_KEY -> tagsFieldValue.setFieldValue(productKey); + case DEVICE_KEY -> tagsFieldValue.setFieldValue(deviceKey); + case DEVICE_NAME -> tagsFieldValue.setFieldValue(deviceName); + case DEVICE_TYPE -> tagsFieldValue.setFieldValue(deviceType); } } } - // 创建设备数据表 + // 3. 创建设备数据表 String tableName = getDeviceTableName(productKey, deviceName); - tdEngineTableService.createTable(TdTableDO.builder().build() + tdEngineDDLMapper.createTable(TdTableDO.builder().build() .setDataBaseName(dataBaseName) .setSuperTableName(superTableName) .setTableName(tableName) @@ -184,8 +197,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @return 数据库名称 */ private String getDatabaseName() { - // TODO @haohao:可以使用 StrUtil.subAftetLast 这种方法 - return url.substring(url.lastIndexOf("/") + 1); + return StrUtil.subAfter(url, "/", true); } /** @@ -198,9 +210,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private static String getProductPropertySTableName(Integer deviceType, String productKey) { // TODO @haohao:枚举下,会好点哈。 return switch (deviceType) { - case 1 -> String.format("gateway_sub_%s", productKey).toLowerCase(); - case 2 -> String.format("gateway_%s", productKey).toLowerCase(); - default -> String.format("device_%s", productKey).toLowerCase(); + case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); + case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); + default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); }; } @@ -212,7 +224,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @return 设备表名 */ private static String getDeviceTableName(String productKey, String deviceName) { - return String.format("device_%s_%s", productKey.toLowerCase(), deviceName.toLowerCase()); + return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java deleted file mode 100644 index 0995b07977..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterService.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; - -/** - * TD 引擎的数据写入 Service 接口 - */ -public interface TdEngineDataWriterService { - - /** - * 插入数据 - 指定列插入数据 - * - * @param table 数据 - * dataBaseName 数据库名 - * tableName 表名 - * columns 列 - */ - void insertData(TdTableDO table); -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java deleted file mode 100644 index f4dd2c8fd7..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDataWriterServiceImpl.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDataWriterMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -/** - * TD 引擎的数据写入 Service 实现类 - */ -@Service -public class TdEngineDataWriterServiceImpl implements TdEngineDataWriterService { - - @Resource - private TdEngineDataWriterMapper tdEngineDataWriterMapper; - - @Override - public void insertData(TdTableDO table) { - tdEngineDataWriterMapper.insertData(table); - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java deleted file mode 100644 index d895262cca..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseService.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -/** - * TD 引擎的数据库 Service 接口 - */ -public interface TdEngineDatabaseService { - - /** - * 创建数据库 - * - * @param dataBaseName 数据库名称 - */ - void createDatabase(String dataBaseName); -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java deleted file mode 100644 index c880db8a93..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineDatabaseServiceImpl.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDatabaseMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -/** - * TD 引擎的数据库 Service 实现类 - */ -@Service -@Slf4j -public class TdEngineDatabaseServiceImpl implements TdEngineDatabaseService { - - @Resource - private TdEngineDatabaseMapper tdEngineDatabaseMapper; - - @Override - public void createDatabase(String dataBaseName) { - tdEngineDatabaseMapper.createDatabase(dataBaseName); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java deleted file mode 100644 index 8d07b43344..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryService.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; - -import java.util.List; -import java.util.Map; - -/** - * TD 引擎的查询 Service 接口 - */ -public interface TdEngineQueryService { - - /** - * 获取历史数据 - * - * @param selectVisualDO 查询条件 - * @return 历史数据列表 - */ - List> getHistoryData(SelectVisualDO selectVisualDO); - - /** - * 获取历史数据条数 - * - * @param selectVisualDO 查询条件 - * @return 数据条数 - */ - Long getHistoryCount(SelectVisualDO selectVisualDO); -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java deleted file mode 100644 index 672a58640a..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineQueryServiceImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineQueryMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -@Service -public class TdEngineQueryServiceImpl implements TdEngineQueryService { - - @Resource - private TdEngineQueryMapper tdEngineQueryMapper; - - @Override - public List> getHistoryData(SelectVisualDO selectVisualDO) { - return tdEngineQueryMapper.getHistoryData(selectVisualDO); - } - - @Override - public Long getHistoryCount(SelectVisualDO selectVisualDO) { - return tdEngineQueryMapper.getHistoryCount(selectVisualDO); - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java deleted file mode 100644 index df6290a983..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableService.java +++ /dev/null @@ -1,123 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; - -import java.util.List; -import java.util.Map; - -/** - * TD 引擎的超级表 Service 接口 - */ -public interface TdEngineSuperTableService { - - /** - * 创建超级表 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * columns 列信息 - * tags 标签信息 - */ - void createSuperTable(TdTableDO superTable); - - /** - * 查看超级表 - 显示当前数据库下的所有超级表信息 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - */ - - List> showSuperTables(TdTableDO superTable); - - /** - * 查看超级表 - 获取超级表的结构信息 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - */ - List> describeSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 增加列 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * column 列信息 - */ - void addColumnForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 删除列 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * column 列信息 - */ - void dropColumnForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 修改列宽 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * column 列信息 - */ - void modifyColumnWidthForSuperTable(TdTableDO superTable); - - - /** - * 修改超级表 - 为超级表添加标签 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tag 标签信息 - */ - void addTagForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 为超级表删除标签 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tag 标签信息 - */ - void dropTagForSuperTable(TdTableDO superTable); - - /** - * 检查超级表是否存在 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * @return 超级表数量 - */ - Integer checkSuperTableExists(TdTableDO superTable); - - /** - * 为超级表添加列 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * columns 列信息 - */ - void addColumnsForSuperTable(TdTableDO superTable); - - /** - * 为超级表删除列 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * columns 列信息 - */ - void dropColumnsForSuperTable(TdTableDO superTable); -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java deleted file mode 100644 index 60436e7bd1..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineSuperTableServiceImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineSuperTableMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Map; - -/** - * TD 引擎的超级表 Service 实现类 - */ -@Slf4j -@Service -public class TdEngineSuperTableServiceImpl implements TdEngineSuperTableService { - - @Resource - private TdEngineSuperTableMapper tdEngineSuperTableMapper; - - @Override - public void createSuperTable(TdTableDO superTable) { - tdEngineSuperTableMapper.createSuperTable(superTable); - } - - @Override - public List> showSuperTables(TdTableDO superTable) { - return tdEngineSuperTableMapper.showSuperTables(superTable); - } - - @Override - public List> describeSuperTable(TdTableDO superTable) { - return tdEngineSuperTableMapper.describeSuperTable(superTable); - } - - @Override - public void addColumnForSuperTable(TdTableDO superTable) { - tdEngineSuperTableMapper.addColumnForSuperTable(superTable); - } - - @Override - public void dropColumnForSuperTable(TdTableDO superTable) { - tdEngineSuperTableMapper.dropColumnForSuperTable(superTable); - } - - @Override - public void modifyColumnWidthForSuperTable(TdTableDO superTable) { - tdEngineSuperTableMapper.modifyColumnWidthForSuperTable(superTable); - } - - @Override - public void addTagForSuperTable(TdTableDO superTable) { - tdEngineSuperTableMapper.addTagForSuperTable(superTable); - } - - @Override - public void dropTagForSuperTable(TdTableDO superTable) { - tdEngineSuperTableMapper.dropTagForSuperTable(superTable); - } - - @Override - public Integer checkSuperTableExists(TdTableDO superTable) { - List> results = tdEngineSuperTableMapper.showSuperTables(superTable); - return results == null || results.isEmpty() ? 0 : results.size(); - } - - @Override - public void addColumnsForSuperTable(TdTableDO superTable) { - for (TdFieldDO column : superTable.getColumns()) { - tdEngineSuperTableMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(superTable.getDataBaseName()) - .superTableName(superTable.getSuperTableName()) - .column(column) - .build()); - } - } - - @Override - public void dropColumnsForSuperTable(TdTableDO superTable) { - for (TdFieldDO column : superTable.getColumns()) { - tdEngineSuperTableMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(superTable.getDataBaseName()) - .superTableName(superTable.getSuperTableName()) - .column(column) - .build()); - } - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java deleted file mode 100644 index 0781597848..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableService.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; - -/** - * TD 引擎的表 Service 接口 - */ -public interface TdEngineTableService { - - /** - * 创建表 - 创建超级表的子表 - * - * @param table 表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tableName 子表名称 - * tags TAG 字段 - */ - void createTable(TdTableDO table); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java deleted file mode 100644 index cdca47888a..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/TdEngineTableServiceImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineTableMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -/** - * TD 引擎的表 Service 实现类 - */ -@Slf4j -@Service -public class TdEngineTableServiceImpl implements TdEngineTableService { - - @Resource - private TdEngineTableMapper tdEngineTableMapper; - - @Override - public void createTable(TdTableDO table) { - tdEngineTableMapper.createTable(table); - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml similarity index 70% rename from yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml rename to yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml index 37cf719101..091b77f642 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineSuperTableMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml @@ -2,7 +2,12 @@ - + + + + + CREATE DATABASE IF NOT EXISTS ${dataBaseName} + @@ -68,4 +73,29 @@ ALTER STABLE ${dataBaseName}.${superTableName} DROP TAG ${tag.fieldName} - + + + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName} + TAGS + + #{item.fieldValue} + + + + + + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName} + + #{item.fieldName} + + TAGS + + #{item.fieldValue} + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml similarity index 68% rename from yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml rename to yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml index f4361bc67f..0443a826b2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineQueryMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml @@ -2,7 +2,21 @@ - + + + + + INSERT INTO ${dataBaseName}.${tableName} + + ${item.fieldName} + + VALUES + + #{item.fieldValue} + + SELECT COUNT(0) AS count FROM ${dataBaseName}.${tableName} @@ -20,14 +34,14 @@ - SELECT LAST(time), * FROM ${tableName} WHERE device_id = #{deviceId} - - - - - SELECT COUNT(time) FROM ${dataBaseName}.${tableName} WHERE time BETWEEN #{startTime} AND #{endTime} AND ${fieldName} IS NOT NULL - + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml deleted file mode 100644 index dd38cd08ec..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDataWriterMapper.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - INSERT INTO ${dataBaseName}.${tableName} - - ${item.fieldName} - - VALUES - - #{item.fieldValue} - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml deleted file mode 100644 index 919c301f73..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDatabaseMapper.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - CREATE DATABASE IF NOT EXISTS ${dataBaseName} - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml deleted file mode 100644 index 867ef8f437..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineTableMapper.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} - USING ${dataBaseName}.${superTableName} - TAGS - - #{item.fieldValue} - - - - - - CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} - USING ${dataBaseName}.${superTableName} - - #{item.fieldName} - - TAGS - - #{item.fieldValue} - - - - \ No newline at end of file From ce919d12d1b079ace85c70004bd6553053b76667 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 5 Dec 2024 21:45:48 +0800 Subject: [PATCH 013/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9Atdengine=20=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/controller/admin/device/IotDeviceDataController.java | 2 ++ .../yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java | 1 - .../module/iot/service/device/IotDeviceDataServiceImpl.java | 2 +- .../module/iot/service/device/IotDeviceServiceImpl.java | 2 +- .../service/tdengine/IotThingModelMessageServiceImpl.java | 5 +---- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 72fdda8c83..77bc78c665 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -31,6 +31,7 @@ public class IotDeviceDataController { @Resource private IotDeviceDataService deviceDataService; + // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") @Operation(summary = "获取设备属性最新数据") public CommonResult> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { @@ -38,6 +39,7 @@ public class IotDeviceDataController { return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); } + // TODO @浩浩:这里的 /history-page 包括方法名。 @GetMapping("/history") @Operation(summary = "获取设备属性历史数据") public CommonResult> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java index 640255940d..3d7aa84ddd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import com.baomidou.dynamic.datasource.annotation.DS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 7d10977f90..d68287cd4b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -133,7 +133,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { } private static String getDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); + return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 58918c1307..bb302a22f7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -139,13 +139,13 @@ public class IotDeviceServiceImpl implements IotDeviceService { * @return 生成的 MQTT Password */ private String generateMqttPassword() { + // TODO @浩浩:这里的 StrUtil 随机字符串? SecureRandom secureRandom = new SecureRandom(); byte[] passwordBytes = new byte[32]; // 256 位的随机数 secureRandom.nextBytes(passwordBytes); return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes); } - /** * 生成唯一的 DeviceName * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 92a30ebb0d..9ffb477150 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -66,8 +66,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() - .setId(device.getId()) - .setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); + .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); } // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 @@ -77,7 +76,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ return; } - // 3. 过滤并收集有效的属性字段,缓存设备属性 List schemaFieldValues = filterAndCollectValidFields(params, functionList, device, thingModelMessage.getTime()); if (schemaFieldValues.size() == 1) { // 仅有时间字段,无需保存 @@ -165,7 +163,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 2.1 过滤出 TAG 类型的字段 List> taggedNotesList = CollectionUtils.filterList(maps, map -> TAG_NOTE.equals(map.get(NOTE))); - // 2.2 解析字段信息 tagsFieldValues = FieldParser.parse(taggedNotesList.stream() .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) From 3a2c691af085c4e649373961e827627b8fcfaf9a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 7 Dec 2024 16:42:56 +0800 Subject: [PATCH 014/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E4=BA=A7=E5=93=81=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E7=9A=84=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 9 ++- .../product/IotProductCategoryController.java | 72 +++++++++++++++++++ .../admin/product/vo/IotProductSaveReqVO.java | 6 +- .../category/IotProductCategoryPageReqVO.java | 27 +++++++ .../vo/category/IotProductCategoryRespVO.java | 33 +++++++++ .../category/IotProductCategorySaveReqVO.java | 29 ++++++++ .../product/IotProductCategoryDO.java | 48 +++++++++++++ .../product/IotProductCategoryMapper.java | 25 +++++++ .../product/IotProductCategoryService.java | 54 ++++++++++++++ .../IotProductCategoryServiceImpl.java | 70 ++++++++++++++++++ 10 files changed, 369 insertions(+), 4 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index be9d28ecb0..1e947e6332 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -9,20 +9,20 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; */ public interface ErrorCodeConstants { - // ========== IoT 产品相关 1-050-001-000 ============ + // ========== 产品相关 1-050-001-000 ============ ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_050_001_000, "产品不存在"); ErrorCode PRODUCT_IDENTIFICATION_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); ErrorCode PRODUCT_STATUS_NOT_ALLOW_FUNCTION = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); - // ========== IoT 产品物模型 1-050-002-000 ============ + // ========== 产品物模型 1-050-002-000 ============ ErrorCode THINK_MODEL_FUNCTION_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); ErrorCode THINK_MODEL_FUNCTION_EXISTS_BY_PRODUCT_KEY = new ErrorCode(1_050_002_001, "ProductKey 对应的产品物模型已存在"); ErrorCode THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS = new ErrorCode(1_050_002_002, "存在重复的功能标识符。"); ErrorCode THINK_MODEL_FUNCTION_NAME_EXISTS = new ErrorCode(1_050_002_003, "存在重复的功能名称。"); ErrorCode THINK_MODEL_FUNCTION_IDENTIFIER_INVALID = new ErrorCode(1_050_002_003, "产品物模型标识无效"); - // ========== IoT 设备 1-050-003-000 ============ + // ========== 设备 1-050-003-000 ============ ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一"); ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除"); @@ -30,4 +30,7 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_PRODUCT_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_004, "产品不能修改"); ErrorCode DEVICE_INVALID_DEVICE_STATUS = new ErrorCode(1_050_003_005, "无效的设备状态"); + // ========== 产品分类 1-050-004-000 ========== + ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java new file mode 100644 index 0000000000..b365ddea10 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; +import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 产品分类") +@RestController +@RequestMapping("/iot/product-category") +@Validated +public class IotProductCategoryController { + + @Resource + private IotProductCategoryService productCategoryService; + + @PostMapping("/create") + @Operation(summary = "创建IoT 产品分类") + @PreAuthorize("@ss.hasPermission('iot:product-category:create')") + public CommonResult createProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO createReqVO) { + return success(productCategoryService.createProductCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新IoT 产品分类") + @PreAuthorize("@ss.hasPermission('iot:product-category:update')") + public CommonResult updateProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO updateReqVO) { + productCategoryService.updateProductCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除IoT 产品分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:product-category:delete')") + public CommonResult deleteProductCategory(@RequestParam("id") Long id) { + productCategoryService.deleteProductCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得IoT 产品分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:product-category:query')") + public CommonResult getProductCategory(@RequestParam("id") Long id) { + IotProductCategoryDO productCategory = productCategoryService.getProductCategory(id); + return success(BeanUtils.toBean(productCategory, IotProductCategoryRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得IoT 产品分类分页") + @PreAuthorize("@ss.hasPermission('iot:product-category:query')") + public CommonResult> getProductCategoryPage(@Valid IotProductCategoryPageReqVO pageReqVO) { + PageResult pageResult = productCategoryService.getProductCategoryPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotProductCategoryRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java index 254b6b9da2..7577505443 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java @@ -14,9 +14,13 @@ public class IotProductSaveReqVO { @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.AUTO, example = "1") private Long id; - @Schema(description = "产品Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc") + @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc") private String productKey; + // TODO 芋艿:品类 + + // TODO 芋艿:【待确定】保活时长、产品图标、产品图片 + @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温湿度") @NotEmpty(message = "产品名称不能为空") private String name; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java new file mode 100644 index 0000000000..90d338a898 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 产品分类分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class IotProductCategoryPageReqVO extends PageParam { + + @Schema(description = "分类名字", example = "王五") + private String name; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java new file mode 100644 index 0000000000..d684b0215a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - IoT 产品分类 Response VO") +@Data +public class IotProductCategoryRespVO { + + @Schema(description = "分类 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25284") + private Long id; + + @Schema(description = "分类名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + @Schema(description = "分类排序") + private Integer sort; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "分类描述", example = "随便") + private String description; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java new file mode 100644 index 0000000000..a7b2fe4271 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 产品分类新增/修改 Request VO") +@Data +public class IotProductCategorySaveReqVO { + + @Schema(description = "分类 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25284") + private Long id; + + @Schema(description = "分类名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "分类名字不能为空") + private String name; + + @Schema(description = "分类排序") + private Integer sort; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分类状态不能为空") + private Integer status; + + @Schema(description = "分类描述", example = "随便") + private String description; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java new file mode 100644 index 0000000000..a6510488ce --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.product; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * IoT 产品分类 DO + * + * @author 芋道源码 + */ +@TableName("iot_product_category") +@KeySequence("iot_product_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotProductCategoryDO extends BaseDO { + + /** + * 分类 ID + */ + @TableId + private Long id; + /** + * 分类名字 + */ + private String name; + /** + * 分类排序 + */ + private Integer sort; + /** + * 分类状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 分类描述 + */ + private String description; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java new file mode 100644 index 0000000000..845eded142 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * IoT 产品分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotProductCategoryMapper extends BaseMapperX { + + default PageResult selectPage(IotProductCategoryPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotProductCategoryDO::getName, reqVO.getName()) + .betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(IotProductCategoryDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java new file mode 100644 index 0000000000..9830ec8b9f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.iot.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; +import jakarta.validation.Valid; + +/** + * IoT 产品分类 Service 接口 + * + * @author 芋道源码 + */ +public interface IotProductCategoryService { + + /** + * 创建IoT 产品分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProductCategory(@Valid IotProductCategorySaveReqVO createReqVO); + + /** + * 更新IoT 产品分类 + * + * @param updateReqVO 更新信息 + */ + void updateProductCategory(@Valid IotProductCategorySaveReqVO updateReqVO); + + /** + * 删除IoT 产品分类 + * + * @param id 编号 + */ + void deleteProductCategory(Long id); + + /** + * 获得IoT 产品分类 + * + * @param id 编号 + * @return IoT 产品分类 + */ + IotProductCategoryDO getProductCategory(Long id); + + /** + * 获得IoT 产品分类分页 + * + * @param pageReqVO 分页查询 + * @return IoT 产品分类分页 + */ + PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java new file mode 100644 index 0000000000..579d27cfbf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.iot.service.product; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; +import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS; + +/** + * IoT 产品分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class IotProductCategoryServiceImpl implements IotProductCategoryService { + + @Resource + private IotProductCategoryMapper productCategoryMapper; + + @Override + public Long createProductCategory(IotProductCategorySaveReqVO createReqVO) { + // 插入 + IotProductCategoryDO productCategory = BeanUtils.toBean(createReqVO, IotProductCategoryDO.class); + productCategoryMapper.insert(productCategory); + // 返回 + return productCategory.getId(); + } + + @Override + public void updateProductCategory(IotProductCategorySaveReqVO updateReqVO) { + // 校验存在 + validateProductCategoryExists(updateReqVO.getId()); + // 更新 + IotProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, IotProductCategoryDO.class); + productCategoryMapper.updateById(updateObj); + } + + @Override + public void deleteProductCategory(Long id) { + // 校验存在 + validateProductCategoryExists(id); + // 删除 + productCategoryMapper.deleteById(id); + } + + private void validateProductCategoryExists(Long id) { + if (productCategoryMapper.selectById(id) == null) { + throw exception(PRODUCT_CATEGORY_NOT_EXISTS); + } + } + + @Override + public IotProductCategoryDO getProductCategory(Long id) { + return productCategoryMapper.selectById(id); + } + + @Override + public PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO) { + return productCategoryMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file From 9b9fd30c90bfb88d1ad61ef314ffab73e18264ea Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 7 Dec 2024 18:50:31 +0800 Subject: [PATCH 015/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91IoT=EF=BC=9A=E4=BA=A7=E5=93=81=E6=94=BE?= =?UTF-8?q?=E5=88=B0=20product=20=E5=AD=90=E7=9B=AE=E5=BD=95=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/product/IotProductController.java | 13 +++--- .../product/vo/IotProductSimpleRespVO.java | 16 ------- .../vo/{ => product}/IotProductPageReqVO.java | 2 +- .../vo/{ => product}/IotProductRespVO.java | 43 +++++++++++-------- .../vo/{ => product}/IotProductSaveReqVO.java | 28 +++++++----- .../dal/dataobject/product/IotProductDO.java | 15 +++++-- .../dal/mysql/product/IotProductMapper.java | 2 +- .../service/product/IotProductService.java | 4 +- .../product/IotProductServiceImpl.java | 5 +-- .../dal/dataobject/notify/PayNotifyLogDO.java | 2 +- .../dataobject/notify/PayNotifyTaskDO.java | 4 +- 11 files changed, 68 insertions(+), 66 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/{ => product}/IotProductPageReqVO.java (88%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/{ => product}/IotProductRespVO.java (85%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/{ => product}/IotProductSaveReqVO.java (82%) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java index e7f41d91dc..a4d111a99e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java @@ -3,10 +3,9 @@ package cn.iocoder.yudao.module.iot.controller.admin.product; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSimpleRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import io.swagger.v3.oas.annotations.Operation; @@ -21,6 +20,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - IoT 产品") @RestController @@ -86,9 +86,10 @@ public class IotProductController { @GetMapping("/simple-list") @Operation(summary = "获得所有产品列表") @PreAuthorize("@ss.hasPermission('iot:product:query')") - public CommonResult> getSimpleProductList() { + public CommonResult> getSimpleProductList() { List list = productService.getProductList(); - return success(BeanUtils.toBean(list, IotProductSimpleRespVO.class)); + return success(convertList(list, product -> // 只返回 id、name 字段 + new IotProductRespVO().setId(product.getId()).setName(product.getName()))); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java deleted file mode 100644 index 83855eaaf8..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 产品 Response VO") -@Data -public class IotProductSimpleRespVO { - - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26087") - private Long id; - - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") - private String name; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java similarity index 88% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java index 3437f563fd..d54adec484 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo; +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java index 0958b3e844..65d26ff5b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo; +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; @@ -20,34 +20,23 @@ public class IotProductRespVO { @ExcelProperty("产品名称") private String name; - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("产品标识") private String productKey; - @Schema(description = "接入网关协议", example = "2") - @ExcelProperty("接入网关协议") - private Integer protocolType; - - @Schema(description = "协议编号(脚本解析 id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "13177") - @ExcelProperty("协议编号(脚本解析 id)") - private Long protocolId; - - @Schema(description = "产品所属品类标识符", example = "14237") - @ExcelProperty("产品所属品类标识符") + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long categoryId; + @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") + private String icon; + + @Schema(description = "产品图标", example = "https://iocoder.cn/1.png") + private String picUrl; + @Schema(description = "产品描述", example = "你猜") @ExcelProperty("产品描述") private String description; - @Schema(description = "数据校验级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("数据校验级别") - private Integer validateType; - @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @ExcelProperty("产品状态") private Integer status; @@ -60,8 +49,24 @@ public class IotProductRespVO { @ExcelProperty("联网方式") private Integer netType; + @Schema(description = "接入网关协议", example = "2") + @ExcelProperty("接入网关协议") + private Integer protocolType; + + @Schema(description = "协议编号(脚本解析 id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "13177") + @ExcelProperty("协议编号(脚本解析 id)") + private Long protocolId; + @Schema(description = "数据格式") @ExcelProperty("数据格式") private Integer dataFormat; + @Schema(description = "数据校验级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("数据校验级别") + private Integer validateType; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java index 7577505443..31890beb7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.product.vo; +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.product.*; @@ -14,17 +14,26 @@ public class IotProductSaveReqVO { @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.AUTO, example = "1") private Long id; - @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc") - private String productKey; - - // TODO 芋艿:品类 - - // TODO 芋艿:【待确定】保活时长、产品图标、产品图片 - @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温湿度") @NotEmpty(message = "产品名称不能为空") private String name; + @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc") + private String productKey; + + @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "产品分类编号不能为空") + private Long categoryId; + + @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") + private String icon; + + @Schema(description = "产品图标", example = "https://iocoder.cn/1.png") + private String picUrl; + + @Schema(description = "产品描述", example = "描述") + private String description; + @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @InEnum(value = IotProductDeviceTypeEnum.class, message = "设备类型必须是 {value}") @NotNull(message = "设备类型不能为空") @@ -48,7 +57,4 @@ public class IotProductSaveReqVO { @NotNull(message = "数据校验级别不能为空") private Integer validateType; - @Schema(description = "产品描述", example = "描述") - private String description; - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java index eef466edad..34fd35706b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java @@ -22,7 +22,7 @@ import lombok.*; public class IotProductDO extends BaseDO { /** - * 产品ID + * 产品 ID */ @TableId private Long id; @@ -30,17 +30,24 @@ public class IotProductDO extends BaseDO { * 产品名称 */ private String name; - // TODO @haohao:这个字段,要不改成 identifier,和阿里云更统一些 /** * 产品标识 */ private String productKey; /** - * 产品所属品类编号 + * 产品分类编号 *

- * TODO 外键:后续加 + * 关联 {@link IotProductCategoryDO#getId()} */ private Long categoryId; + /** + * 产品图标 + */ + private String icon; + /** + * 产品图片 + */ + private String picUrl; /** * 产品描述 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index 0341e24921..eac1d29e8b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import org.apache.ibatis.annotations.Mapper; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index 3fff94fd9d..bcbf13ef45 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import jakarta.validation.Valid; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index d0ca092889..259bcaa1f6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; @@ -14,7 +14,6 @@ import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyLogDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyLogDO.java index a482605d52..384628370c 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyLogDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyLogDO.java @@ -44,7 +44,7 @@ public class PayNotifyLogDO extends BaseDO { /** * 支付通知状态 * - * 外键 {@link PayNotifyStatusEnum} + * 枚举 {@link PayNotifyStatusEnum} */ private Integer status; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java index 7bfabad3ff..9f9ee7fefb 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java @@ -52,7 +52,7 @@ public class PayNotifyTaskDO extends TenantBaseDO { /** * 通知类型 * - * 外键 {@link PayNotifyTypeEnum} + * 枚举 {@link PayNotifyTypeEnum} */ private Integer type; /** @@ -73,7 +73,7 @@ public class PayNotifyTaskDO extends TenantBaseDO { /** * 通知状态 * - * 外键 {@link PayNotifyStatusEnum} + * 枚举 {@link PayNotifyStatusEnum} */ private Integer status; /** From a8c87d168accd14f5b266be9815046b1b45ff9db Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 7 Dec 2024 19:50:07 +0800 Subject: [PATCH 016/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E4=BA=A7=E5=93=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=97=B6=EF=BC=8CproductKey=20=E7=94=B1=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=94=9F=E6=88=90=EF=BC=9B=E5=90=8C=E6=97=B6=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20icon=E3=80=81picUrl=20=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 2 +- .../enums/product/IotProductStatusEnum.java | 4 +-- .../product/IotProductCategoryController.java | 24 +++++++++++--- .../product/IotProductCategoryMapper.java | 6 ++++ .../product/IotProductCategoryService.java | 24 ++++++++++---- .../IotProductCategoryServiceImpl.java | 7 ++++ .../product/IotProductServiceImpl.java | 32 ++++--------------- .../IotThinkModelFunctionServiceImpl.java | 2 +- 8 files changed, 60 insertions(+), 41 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 1e947e6332..d1d350d5eb 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -11,7 +11,7 @@ public interface ErrorCodeConstants { // ========== 产品相关 1-050-001-000 ============ ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_050_001_000, "产品不存在"); - ErrorCode PRODUCT_IDENTIFICATION_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); + ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); ErrorCode PRODUCT_STATUS_NOT_ALLOW_FUNCTION = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java index e64a3d6789..7b4d90a5a4 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java @@ -18,12 +18,12 @@ public enum IotProductStatusEnum implements IntArrayValuable { UNPUBLISHED(0, "开发中"), PUBLISHED(1, "已发布"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductStatusEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductStatusEnum::getStatus).toArray(); /** * 类型 */ - private final Integer type; + private final Integer status; /** * 描述 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java index b365ddea10..bc1c1fbf2b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.product; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -17,7 +18,10 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - IoT 产品分类") @RestController @@ -29,14 +33,14 @@ public class IotProductCategoryController { private IotProductCategoryService productCategoryService; @PostMapping("/create") - @Operation(summary = "创建IoT 产品分类") + @Operation(summary = "创建产品分类") @PreAuthorize("@ss.hasPermission('iot:product-category:create')") public CommonResult createProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO createReqVO) { return success(productCategoryService.createProductCategory(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新IoT 产品分类") + @Operation(summary = "更新产品分类") @PreAuthorize("@ss.hasPermission('iot:product-category:update')") public CommonResult updateProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO updateReqVO) { productCategoryService.updateProductCategory(updateReqVO); @@ -44,7 +48,7 @@ public class IotProductCategoryController { } @DeleteMapping("/delete") - @Operation(summary = "删除IoT 产品分类") + @Operation(summary = "删除产品分类") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('iot:product-category:delete')") public CommonResult deleteProductCategory(@RequestParam("id") Long id) { @@ -53,7 +57,7 @@ public class IotProductCategoryController { } @GetMapping("/get") - @Operation(summary = "获得IoT 产品分类") + @Operation(summary = "获得产品分类") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('iot:product-category:query')") public CommonResult getProductCategory(@RequestParam("id") Long id) { @@ -62,11 +66,21 @@ public class IotProductCategoryController { } @GetMapping("/page") - @Operation(summary = "获得IoT 产品分类分页") + @Operation(summary = "获得产品分类分页") @PreAuthorize("@ss.hasPermission('iot:product-category:query')") public CommonResult> getProductCategoryPage(@Valid IotProductCategoryPageReqVO pageReqVO) { PageResult pageResult = productCategoryService.getProductCategoryPage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotProductCategoryRespVO.class)); } + @GetMapping("/simple-list") + @Operation(summary = "获得所有产品分类列表") + @PreAuthorize("@ss.hasPermission('iot:product-category:query')") + public CommonResult> getSimpleProductCategoryList() { + List list = productCategoryService.getProductCategoryListByStatus( + CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, category -> + new IotProductCategoryRespVO().setId(category.getId()).setName(category.getName()))); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java index 845eded142..8b1744af07 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProdu import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * IoT 产品分类 Mapper * @@ -22,4 +24,8 @@ public interface IotProductCategoryMapper extends BaseMapperX selectListByStatus(Integer status) { + return selectList(IotProductCategoryDO::getStatus, status); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java index 9830ec8b9f..72bdefd8fb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProdu import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import jakarta.validation.Valid; +import java.util.List; + /** * IoT 产品分类 Service 接口 * @@ -14,7 +16,7 @@ import jakarta.validation.Valid; public interface IotProductCategoryService { /** - * 创建IoT 产品分类 + * 创建产品分类 * * @param createReqVO 创建信息 * @return 编号 @@ -22,33 +24,41 @@ public interface IotProductCategoryService { Long createProductCategory(@Valid IotProductCategorySaveReqVO createReqVO); /** - * 更新IoT 产品分类 + * 更新产品分类 * * @param updateReqVO 更新信息 */ void updateProductCategory(@Valid IotProductCategorySaveReqVO updateReqVO); /** - * 删除IoT 产品分类 + * 删除产品分类 * * @param id 编号 */ void deleteProductCategory(Long id); /** - * 获得IoT 产品分类 + * 获得产品分类 * * @param id 编号 - * @return IoT 产品分类 + * @return 产品分类 */ IotProductCategoryDO getProductCategory(Long id); /** - * 获得IoT 产品分类分页 + * 获得产品分类分页 * * @param pageReqVO 分页查询 - * @return IoT 产品分类分页 + * @return 产品分类分页 */ PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO); + /** + * 获得产品分类列表,根据状态 + * + * @param status 状态 + * @return 产品分类列表 + */ + List getProductCategoryListByStatus(Integer status); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index 579d27cfbf..fa23c0fe72 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -10,6 +10,8 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.List; + import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS; @@ -67,4 +69,9 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService return productCategoryMapper.selectPage(pageReqVO); } + @Override + public List getProductCategoryListByStatus(Integer status) { + return productCategoryMapper.selectListByStatus(status); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 259bcaa1f6..b1d2d666ba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.service.product; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; @@ -11,14 +10,12 @@ import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; - import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.Objects; -import java.util.UUID; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @@ -42,31 +39,16 @@ public class IotProductServiceImpl implements IotProductService { @Override public Long createProduct(IotProductSaveReqVO createReqVO) { // 1. 生成 ProductKey - createProductKey(createReqVO); + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } // 2. 插入 - IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class); + IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) + .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); productMapper.insert(product); return product.getId(); } - /** - * 创建 ProductKey - * - * @param createReqVO 创建信息 - */ - private void createProductKey(IotProductSaveReqVO createReqVO) { - String productKey = createReqVO.getProductKey(); - // 1. productKey为空,生成随机的 11 位字符串 - if (StrUtil.isEmpty(productKey)) { - productKey = UUID.randomUUID().toString().replace("-", "").substring(0, 11); - } - // 2. 校验唯一性 - if (productMapper.selectByProductKey(productKey) != null) { - throw exception(PRODUCT_IDENTIFICATION_EXISTS); - } - createReqVO.setProductKey(productKey); - } - @Override public void updateProduct(IotProductSaveReqVO updateReqVO) { updateReqVO.setProductKey(null); // 不更新产品标识 @@ -98,7 +80,7 @@ public class IotProductServiceImpl implements IotProductService { } private void validateProductStatus(IotProductDO iotProductDO) { - if (Objects.equals(iotProductDO.getStatus(), IotProductStatusEnum.PUBLISHED.getType())) { + if (Objects.equals(iotProductDO.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { throw exception(PRODUCT_STATUS_NOT_DELETE); } } @@ -121,7 +103,7 @@ public class IotProductServiceImpl implements IotProductService { // 2. 更新 IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); // 3. 产品是发布状态 - if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getType())) { + if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建超级表数据模型 thinkModelFunctionService.createSuperTableDataModel(id); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 404a2aaa05..bfccfb45a1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -82,7 +82,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe private void validateProductStatus(Long createReqVO) { IotProductDO product = productService.getProduct(createReqVO); - if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getType())) { + if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { throw exception(PRODUCT_STATUS_NOT_ALLOW_FUNCTION); } } From 9841c869a2ef457eab3be7ff3224c32f7ac78126 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 7 Dec 2024 20:17:11 +0800 Subject: [PATCH 017/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E4=BA=A7=E5=93=81=E8=AF=A6?= =?UTF-8?q?=E6=83=85=EF=BC=8C=E4=BC=98=E5=8C=96=E7=9B=B8=E5=85=B3=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/product/IotProductController.java | 22 +++++++++++++++++-- .../product/vo/product/IotProductRespVO.java | 3 +++ .../product/IotProductCategoryService.java | 22 +++++++++++++++++++ .../IotProductCategoryServiceImpl.java | 10 +++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java index a4d111a99e..50ee44faf3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java @@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.iot.controller.admin.product; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -18,6 +21,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -30,6 +34,8 @@ public class IotProductController { @Resource private IotProductService productService; + @Resource + private IotProductCategoryService categoryService; @PostMapping("/create") @Operation(summary = "创建产品") @@ -72,7 +78,13 @@ public class IotProductController { @PreAuthorize("@ss.hasPermission('iot:product:query')") public CommonResult getProduct(@RequestParam("id") Long id) { IotProductDO product = productService.getProduct(id); - return success(BeanUtils.toBean(product, IotProductRespVO.class)); + // 拼接数据 + IotProductCategoryDO category = categoryService.getProductCategory(product.getCategoryId()); + return success(BeanUtils.toBean(product, IotProductRespVO.class, bean -> { + if (category != null) { + bean.setCategoryName(category.getName()); + } + })); } @GetMapping("/page") @@ -80,7 +92,13 @@ public class IotProductController { @PreAuthorize("@ss.hasPermission('iot:product:query')") public CommonResult> getProductPage(@Valid IotProductPageReqVO pageReqVO) { PageResult pageResult = productService.getProductPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotProductRespVO.class)); + // 拼接数据 + Map categoryMap = categoryService.getProductCategoryMap( + convertList(pageResult.getList(), IotProductDO::getCategoryId)); + return success(BeanUtils.toBean(pageResult, IotProductRespVO.class, bean -> { + MapUtils.findAndThen(categoryMap, bean.getCategoryId(), + category -> bean.setCategoryName(category.getName())); + })); } @GetMapping("/simple-list") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java index 65d26ff5b0..814b548f1a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java @@ -27,6 +27,9 @@ public class IotProductRespVO { @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long categoryId; + @Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String categoryName; + @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") private String icon; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java index 72bdefd8fb..e44b6d4878 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -6,7 +6,11 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProdu import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import jakarta.validation.Valid; +import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * IoT 产品分类 Service 接口 @@ -45,6 +49,24 @@ public interface IotProductCategoryService { */ IotProductCategoryDO getProductCategory(Long id); + /** + * 获得产品分类列表 + * + * @param ids 编号 + * @return 产品分类列表 + */ + List getProductCategoryList(Collection ids); + + /** + * 获得产品分类 Map + * + * @param ids 编号 + * @return 产品分类 Map + */ + default Map getProductCategoryMap(Collection ids) { + return convertMap(getProductCategoryList(ids), IotProductCategoryDO::getId); + } + /** * 获得产品分类分页 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index fa23c0fe72..c531b14504 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.product; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; @@ -10,6 +11,7 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -64,6 +66,14 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService return productCategoryMapper.selectById(id); } + @Override + public List getProductCategoryList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return CollUtil.newArrayList(); + } + return productCategoryMapper.selectBatchIds(ids); + } + @Override public PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO) { return productCategoryMapper.selectPage(pageReqVO); From db9c485285d99fef822557582fcf00fd9f26bd7e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 7 Dec 2024 20:48:19 +0800 Subject: [PATCH 018/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E4=BA=A7=E5=93=81=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/DictTypeConstants.java | 16 +++++++++++++ .../admin/product/IotProductController.java | 19 +++++++++++++++ .../product/vo/product/IotProductRespVO.java | 24 ++++++++++++++----- 3 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java new file mode 100644 index 0000000000..b5c34f14b7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.enums; + +/** + * IoT 字典类型的枚举类 + * + * @author 芋道源码 + */ +public class DictTypeConstants { + + public static final String PRODUCT_STATUS = "iot_product_status"; + public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type"; + public static final String NET_TYPE = "iot_net_type"; + public static final String PROTOCOL_TYPE = "iot_protocol_type"; + public static final String DATA_FORMAT = "iot_data_format"; + public static final String VALIDATE_TYPE = "iot_validate_type"; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java index 50ee44faf3..c34571471b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.module.iot.controller.admin.product; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; @@ -15,14 +18,17 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.io.IOException; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -101,6 +107,19 @@ public class IotProductController { })); } + @GetMapping("/export-excel") + @Operation(summary = "导出产品 Excel") + @PreAuthorize("@ss.hasPermission('iot:product:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportProductExcel(@Valid IotProductPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + CommonResult> result = getProductPage(exportReqVO); + // 导出 Excel + ExcelUtils.write(response, "产品.xls", "数据", IotProductRespVO.class, + result.getData().getList()); + } + @GetMapping("/simple-list") @Operation(summary = "获得所有产品列表") @PreAuthorize("@ss.hasPermission('iot:product:query')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java index 814b548f1a..6637a67ff0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.iot.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -28,12 +31,15 @@ public class IotProductRespVO { private Long categoryId; @Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("产品分类") private String categoryName; @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") + @ExcelProperty("产品图标") private String icon; @Schema(description = "产品图标", example = "https://iocoder.cn/1.png") + @ExcelProperty("产品图标") private String picUrl; @Schema(description = "产品描述", example = "你猜") @@ -41,19 +47,23 @@ public class IotProductRespVO { private String description; @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("产品状态") + @ExcelProperty(value = "产品状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.PRODUCT_STATUS) private Integer status; @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("设备类型") + @ExcelProperty(value = "设备类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.PRODUCT_DEVICE_TYPE) private Integer deviceType; @Schema(description = "联网方式", example = "2") - @ExcelProperty("联网方式") + @ExcelProperty(value = "联网方式", converter = DictConvert.class) + @DictFormat(DictTypeConstants.NET_TYPE) private Integer netType; @Schema(description = "接入网关协议", example = "2") - @ExcelProperty("接入网关协议") + @ExcelProperty(value = "接入网关协议", converter = DictConvert.class) + @DictFormat(DictTypeConstants.PROTOCOL_TYPE) private Integer protocolType; @Schema(description = "协议编号(脚本解析 id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "13177") @@ -61,11 +71,13 @@ public class IotProductRespVO { private Long protocolId; @Schema(description = "数据格式") - @ExcelProperty("数据格式") + @ExcelProperty(value = "数据格式", converter = DictConvert.class) + @DictFormat(DictTypeConstants.DATA_FORMAT) private Integer dataFormat; @Schema(description = "数据校验级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("数据校验级别") + @ExcelProperty(value = "数据校验级别", converter = DictConvert.class) + @DictFormat(DictTypeConstants.VALIDATE_TYPE) private Integer validateType; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) From afaf98c44f76daa8a18fafa7410991a0b2485889 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 16:12:24 +0800 Subject: [PATCH 019/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=81=E4=BF=AE=E6=94=B9=E6=94=AF=E6=8C=81=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/core/type/LongSetTypeHandler.java | 58 +++++ .../module/iot/enums/ErrorCodeConstants.java | 6 +- .../product/IotProductDeviceTypeEnum.java | 10 + .../admin/device/IotDeviceController.java | 13 + .../device/vo/device/IotDeviceSaveReqVO.java | 12 + .../admin/product/IotProductController.java | 6 +- .../product/vo/product/IotProductRespVO.java | 4 +- .../vo/product/IotProductSaveReqVO.java | 2 +- .../dal/dataobject/device/IotDeviceDO.java | 30 ++- .../iot/dal/mysql/device/IotDeviceMapper.java | 11 + .../product/IotProductCategoryMapper.java | 2 +- .../iot/service/device/IotDeviceService.java | 16 +- .../service/device/IotDeviceServiceImpl.java | 226 ++++++++---------- 13 files changed, 249 insertions(+), 147 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java new file mode 100644 index 0000000000..58d82ecf3f --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/LongSetTypeHandler.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.framework.mybatis.core.type; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; +import org.apache.ibatis.type.TypeHandler; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Set; + +/** + * Set 的类型转换器实现类,对应数据库的 varchar 类型 + * + * @author 芋道源码 + */ +@MappedJdbcTypes(JdbcType.VARCHAR) +@MappedTypes(List.class) +public class LongSetTypeHandler implements TypeHandler> { + + private static final String COMMA = ","; + + @Override + public void setParameter(PreparedStatement ps, int i, Set strings, JdbcType jdbcType) throws SQLException { + // 设置占位符 + ps.setString(i, CollUtil.join(strings, COMMA)); + } + + @Override + public Set getResult(ResultSet rs, String columnName) throws SQLException { + String value = rs.getString(columnName); + return getResult(value); + } + + @Override + public Set getResult(ResultSet rs, int columnIndex) throws SQLException { + String value = rs.getString(columnIndex); + return getResult(value); + } + + @Override + public Set getResult(CallableStatement cs, int columnIndex) throws SQLException { + String value = cs.getString(columnIndex); + return getResult(value); + } + + private Set getResult(String value) { + if (value == null) { + return null; + } + return StrUtils.splitToLongSet(value, COMMA); + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index d1d350d5eb..0d8b7d8bcc 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -26,9 +26,9 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一"); ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除"); - ErrorCode DEVICE_NAME_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_003, "设备名称不能修改"); - ErrorCode DEVICE_PRODUCT_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_004, "产品不能修改"); - ErrorCode DEVICE_INVALID_DEVICE_STATUS = new ErrorCode(1_050_003_005, "无效的设备状态"); + ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在"); + ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); + ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java index 99b75f3fbd..ef1432804a 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java @@ -36,4 +36,14 @@ public enum IotProductDeviceTypeEnum implements IntArrayValuable { return ARRAYS; } + /** + * 判断是否是网关 + * + * @param type 类型 + * @return 是否是网关 + */ + public static boolean isGateway(Integer type) { + return GATEWAY.getType().equals(type); + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index ba68e02328..2dee57d6d9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -18,7 +18,10 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - IoT 设备") @RestController @@ -86,4 +89,14 @@ public class IotDeviceController { return success(deviceService.getDeviceCountByProductId(productId)); } + @GetMapping("/simple-list") + @Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项") + @Parameter(name = "deviceType", description = "设备类型", example = "1") + public CommonResult> getSimpleDeviceList( + @RequestParam(value = "deviceType", required = false) Integer deviceType) { + List list = deviceService.getDeviceList(deviceType); + return success(convertList(list, device -> // 只返回 id、name 字段 + new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index 85fde9d3d9..e4f23d97e2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -10,13 +10,25 @@ public class IotDeviceSaveReqVO { @Schema(description = "设备编号", example = "177") private Long id; + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.AUTO, example = "177") + private String deviceKey; + @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") private String deviceName; @Schema(description = "备注名称", example = "张三") private String nickname; + @Schema(description = "设备序列号", example = "123456") + private String serialNumber; + + @Schema(description = "设备图片", example = "https://iocoder.cn/1.png") + private String picUrl; + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") private Long productId; + @Schema(description = "网关设备 ID", example = "16380") + private Long gatewayId; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java index c34571471b..2d8c856400 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java @@ -121,12 +121,12 @@ public class IotProductController { } @GetMapping("/simple-list") - @Operation(summary = "获得所有产品列表") - @PreAuthorize("@ss.hasPermission('iot:product:query')") + @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") public CommonResult> getSimpleProductList() { List list = productService.getProductList(); return success(convertList(list, product -> // 只返回 id、name 字段 - new IotProductRespVO().setId(product.getId()).setName(product.getName()))); + new IotProductRespVO().setId(product.getId()).setName(product.getName()) + .setDeviceType(product.getDeviceType()))); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java index 6637a67ff0..f674651d51 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java @@ -38,8 +38,8 @@ public class IotProductRespVO { @ExcelProperty("产品图标") private String icon; - @Schema(description = "产品图标", example = "https://iocoder.cn/1.png") - @ExcelProperty("产品图标") + @Schema(description = "产品图片", example = "https://iocoder.cn/1.png") + @ExcelProperty("产品图片") private String picUrl; @Schema(description = "产品描述", example = "你猜") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java index 31890beb7d..268ab7c6fa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java @@ -28,7 +28,7 @@ public class IotProductSaveReqVO { @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") private String icon; - @Schema(description = "产品图标", example = "https://iocoder.cn/1.png") + @Schema(description = "产品图片", example = "https://iocoder.cn/1.png") private String picUrl; @Schema(description = "产品描述", example = "描述") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index d3f6547a1a..6028c25fe0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -1,22 +1,25 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; 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 lombok.*; import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Set; /** * IoT 设备 DO * * @author haohao */ -@TableName("iot_device") +@TableName(value = "iot_device", autoResultMap = true) @KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @@ -47,6 +50,17 @@ public class IotDeviceDO extends BaseDO { * 设备序列号 */ private String serialNumber; + /** + * 设备图片 + */ + private String picUrl; + /** + * 设备分组编号集合 + * + * 关联 TODO 芋艿: + */ + @TableField(typeHandler = LongSetTypeHandler.class) + private Set groupIds; /** * 产品编号 @@ -66,13 +80,6 @@ public class IotDeviceDO extends BaseDO { * 冗余 {@link IotProductDO#getDeviceType()} */ private Integer deviceType; - - /** - * 设备状态 - *

- * 枚举 {@link IotDeviceStatusEnum} - */ - private Integer status; /** * 网关设备编号 *

@@ -82,6 +89,13 @@ public class IotDeviceDO extends BaseDO { */ private Long gatewayId; + /** + * 设备状态 + *

+ * 枚举 {@link IotDeviceStatusEnum} + */ + private Integer status; + /** * 设备状态最后更新时间 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 4d8315eb3c..65af1f17ac 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * IoT 设备 Mapper * @@ -51,4 +53,13 @@ public interface IotDeviceMapper extends BaseMapperX { default Long selectCountByProductId(Long productId) { return selectCount(IotDeviceDO::getProductId, productId); } + + default IotDeviceDO selectByDeviceKey(String deviceKey) { + return selectOne(IotDeviceDO::getDeviceKey, deviceKey); + } + + default List selectList(Integer deviceType) { + return selectList(IotDeviceDO::getDeviceType, deviceType); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java index 8b1744af07..70ad56da80 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java @@ -21,7 +21,7 @@ public interface IotProductCategoryMapper extends BaseMapperX() .likeIfPresent(IotProductCategoryDO::getName, reqVO.getName()) .betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(IotProductCategoryDO::getId)); + .orderByAsc(IotProductCategoryDO::getSort)); } default List selectListByStatus(Integer status) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index ed4c6a60ce..3569c75977 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -1,11 +1,14 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; -import jakarta.validation.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; +import jakarta.validation.Valid; + +import javax.annotation.Nullable; +import java.util.List; /** * IoT 设备 Service 接口 @@ -52,6 +55,14 @@ public interface IotDeviceService { */ PageResult getDevicePage(IotDevicePageReqVO pageReqVO); + /** + * 获得设备列表 + * + * @param deviceType 设备类型 + * @return 设备列表 + */ + List getDeviceList(@Nullable Integer deviceType); + /** * 更新设备状态 * @@ -75,4 +86,5 @@ public interface IotDeviceService { * @return 设备信息 */ IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index bb302a22f7..c882b7420a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -12,18 +12,17 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; +import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import java.security.SecureRandom; +import javax.annotation.Nullable; import java.time.LocalDateTime; -import java.util.Base64; +import java.util.List; import java.util.Objects; -import java.util.UUID; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @@ -40,142 +39,64 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Resource private IotDeviceMapper deviceMapper; + @Resource private IotProductService productService; - /** - * 创建 IoT 设备 - * - * @param createReqVO 创建请求 VO - * @return 设备 ID - */ @Override - @Transactional(rollbackFor = Exception.class) public Long createDevice(IotDeviceSaveReqVO createReqVO) { // 1.1 校验产品是否存在 IotProductDO product = productService.getProduct(createReqVO.getProductId()); if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } - // 1.2 校验设备名称在同一产品下是否唯一 - if (StrUtil.isBlank(createReqVO.getDeviceName())) { - createReqVO.setDeviceName(generateUniqueDeviceName(product.getProductKey())); - } else { - validateDeviceNameUnique(product.getProductKey(), createReqVO.getDeviceName()); + // 1.2 校验设备标识是否唯一 + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { + throw exception(DEVICE_KEY_EXISTS); + } + // 1.3 校验设备名称在同一产品下是否唯一 + if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { + throw exception(DEVICE_NAME_EXISTS); + } + // 1.4 校验父设备是否为合法网关 + if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType()) + && createReqVO.getGatewayId() != null) { + validateGatewayDeviceExists(createReqVO.getGatewayId()); } // 2.1 转换 VO 为 DO - IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class) - .setProductKey(product.getProductKey()) - .setDeviceType(product.getDeviceType()); - // 2.2 生成并设置必要的字段 - device.setDeviceKey(generateUniqueDeviceKey()); - device.setDeviceSecret(generateDeviceSecret()); - device.setMqttClientId(generateMqttClientId()); - device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey())); - device.setMqttPassword(generateMqttPassword()); - // 2.3 设置设备状态为未激活 - device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()); - device.setStatusLastUpdateTime(LocalDateTime.now()); - // 2.4 插入到数据库 + IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> { + o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); + // 生成并设置必要的字段 + o.setDeviceSecret(generateDeviceSecret()) + .setMqttClientId(generateMqttClientId()) + .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey())) + .setMqttPassword(generateMqttPassword()); + // 设置设备状态为未激活 + o.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()).setStatusLastUpdateTime(LocalDateTime.now()); + }); + // 2.2 插入到数据库 deviceMapper.insert(device); return device.getId(); } - /** - * 校验设备名称在同一产品下是否唯一 - * - * @param productKey 产品 Key - * @param deviceName 设备名称 - */ - private void validateDeviceNameUnique(String productKey, String deviceName) { - IotDeviceDO existingDevice = deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); - if (existingDevice != null) { - throw exception(DEVICE_NAME_EXISTS); - } - } - - /** - * 生成唯一的 deviceKey - * - * @return 生成的 deviceKey - */ - private String generateUniqueDeviceKey() { - return UUID.randomUUID().toString(); - } - - /** - * 生成 deviceSecret - * - * @return 生成的 deviceSecret - */ - private String generateDeviceSecret() { - return IdUtil.fastSimpleUUID(); - } - - /** - * 生成 MQTT Client ID - * - * @return 生成的 MQTT Client ID - */ - private String generateMqttClientId() { - return UUID.randomUUID().toString(); - } - - /** - * 生成 MQTT Username - * - * @param deviceName 设备名称 - * @param productKey 产品 Key - * @return 生成的 MQTT Username - */ - private String generateMqttUsername(String deviceName, String productKey) { - return deviceName + "&" + productKey; - } - - /** - * 生成 MQTT Password - * - * @return 生成的 MQTT Password - */ - private String generateMqttPassword() { - // TODO @浩浩:这里的 StrUtil 随机字符串? - SecureRandom secureRandom = new SecureRandom(); - byte[] passwordBytes = new byte[32]; // 256 位的随机数 - secureRandom.nextBytes(passwordBytes); - return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes); - } - - /** - * 生成唯一的 DeviceName - * - * @param productKey 产品标识 - * @return 生成的唯一 DeviceName - */ - private String generateUniqueDeviceName(String productKey) { - for (int i = 0; i < Short.MAX_VALUE; i++) { - String deviceName = IdUtil.fastSimpleUUID().substring(0, 20); - if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) { - return deviceName; - } - } - throw new IllegalArgumentException("生成 DeviceName 失败"); - } - @Override - @Transactional(rollbackFor = Exception.class) public void updateDevice(IotDeviceSaveReqVO updateReqVO) { - // 1. 校验存在 - validateDeviceExists(updateReqVO.getId()); + updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新 + // 1.1 校验存在 + IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); + // 1.2 校验父设备是否为合法网关 + if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType()) + && updateReqVO.getGatewayId() != null) { + validateGatewayDeviceExists(updateReqVO.getGatewayId()); + } // 2. 更新到数据库 - IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class) - .setDeviceName(null).setProductId(null); // 设备名称 和 产品 ID 不能修改 + IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); deviceMapper.updateById(updateObj); } @Override - @Transactional(rollbackFor = Exception.class) public void deleteDevice(Long id) { // 1.1 校验存在 IotDeviceDO device = validateDeviceExists(id); @@ -202,13 +123,24 @@ public class IotDeviceServiceImpl implements IotDeviceService { return device; } - @Override - public IotDeviceDO getDevice(Long id) { + /** + * 校验网关设备是否存在 + * + * @param id 设备 ID + */ + private void validateGatewayDeviceExists(Long id) { IotDeviceDO device = deviceMapper.selectById(id); if (device == null) { - throw exception(DEVICE_NOT_EXISTS); + throw exception(DEVICE_GATEWAY_NOT_EXISTS); } - return device; + if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { + throw exception(DEVICE_NOT_GATEWAY); + } + } + + @Override + public IotDeviceDO getDevice(Long id) { + return deviceMapper.selectById(id); } @Override @@ -216,19 +148,24 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectPage(pageReqVO); } + @Override + public List getDeviceList(@Nullable Integer deviceType) { + return deviceMapper.selectList(deviceType); + } + @Override public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { // 1. 校验存在 IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); // 2.1 更新状态和更新时间 - IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); + IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class) + .setStatusLastUpdateTime(LocalDateTime.now()); // 2.2 更新状态相关时间 if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus()) && Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { // 从未激活到在线,设置激活时间和最后上线时间 - updateDevice.setActiveTime(LocalDateTime.now()); - updateDevice.setLastOnlineTime(LocalDateTime.now()); + updateDevice.setActiveTime(LocalDateTime.now()).setLastOnlineTime(LocalDateTime.now()); } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { // 如果是上线,设置最后上线时间 updateDevice.setLastOnlineTime(LocalDateTime.now()); @@ -236,10 +173,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 如果是离线,设置最后离线时间 updateDevice.setLastOfflineTime(LocalDateTime.now()); } - - // 2.3 设置状态更新时间 - updateDevice.setStatusLastUpdateTime(LocalDateTime.now()); - // 2.4 更新到数据库 + // 2.3 更新到数据库 deviceMapper.updateById(updateDevice); } @@ -254,4 +188,42 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); } + /** + * 生成 deviceSecret + * + * @return 生成的 deviceSecret + */ + private String generateDeviceSecret() { + return IdUtil.fastSimpleUUID(); + } + + /** + * 生成 MQTT Client ID + * + * @return 生成的 MQTT Client ID + */ + private String generateMqttClientId() { + return IdUtil.fastSimpleUUID(); + } + + /** + * 生成 MQTT Username + * + * @param deviceName 设备名称 + * @param productKey 产品 Key + * @return 生成的 MQTT Username + */ + private String generateMqttUsername(String deviceName, String productKey) { + return deviceName + "&" + productKey; + } + + /** + * 生成 MQTT Password + * + * @return 生成的 MQTT Password + */ + private String generateMqttPassword() { + return RandomUtil.randomString(32); + } + } \ No newline at end of file From 9041de2da57bd7fac4af894d5f44f82e20e58cf3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 16:34:26 +0800 Subject: [PATCH 020/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E7=AD=9B=E9=80=89=E5=8E=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/vo/device/IotDevicePageReqVO.java | 52 +------------------ .../iot/dal/mysql/device/IotDeviceMapper.java | 16 +----- 2 files changed, 4 insertions(+), 64 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java index 90ded80898..791ee34b60 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java @@ -8,11 +8,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - IoT 设备分页 Request VO") @Data @@ -20,11 +15,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @ToString(callSuper = true) public class IotDevicePageReqVO extends PageParam { - // TODO @芋艿:需要去掉一些多余的字段; - - @Schema(description = "设备唯一标识符", example = "24602") - private String deviceKey; - @Schema(description = "设备名称", example = "王五") private String deviceName; @@ -34,53 +24,15 @@ public class IotDevicePageReqVO extends PageParam { @Schema(description = "产品编号", example = "26202") private Long productId; - @Schema(description = "产品标识") - private String productKey; - @Schema(description = "设备类型", example = "1") @InEnum(IotProductDeviceTypeEnum.class) private Integer deviceType; - @Schema(description = "网关设备 ID", example = "16380") - private Long gatewayId; - @Schema(description = "设备状态", example = "1") @InEnum(IotDeviceStatusEnum.class) private Integer status; - @Schema(description = "设备状态最后更新时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] statusLastUpdateTime; - - @Schema(description = "最后上线时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] lastOnlineTime; - - @Schema(description = "最后离线时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] lastOfflineTime; - - @Schema(description = "设备激活时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] activeTime; - - @Schema(description = "设备密钥,用于设备认证,需安全存储") - private String deviceSecret; - - @Schema(description = "MQTT 客户端 ID", example = "24602") - private String mqttClientId; - - @Schema(description = "MQTT 用户名", example = "芋艿") - private String mqttUsername; - - @Schema(description = "MQTT 密码") - private String mqttPassword; - - @Schema(description = "认证类型(如一机一密、动态注册)", example = "2") - private String authType; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; + @Schema(description = "设备分组编号", example = "1024") + private Long groupId; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 65af1f17ac..0459c964d9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.mysql.device; +import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -17,27 +18,14 @@ import java.util.List; @Mapper public interface IotDeviceMapper extends BaseMapperX { - // TODO @haohao:可能多余的查询条件,要去掉哈 default PageResult selectPage(IotDevicePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey()) .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName()) .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId()) - .eqIfPresent(IotDeviceDO::getProductKey, reqVO.getProductKey()) .eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType()) .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname()) - .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId()) .eqIfPresent(IotDeviceDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(IotDeviceDO::getStatusLastUpdateTime, reqVO.getStatusLastUpdateTime()) - .betweenIfPresent(IotDeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime()) - .betweenIfPresent(IotDeviceDO::getLastOfflineTime, reqVO.getLastOfflineTime()) - .betweenIfPresent(IotDeviceDO::getActiveTime, reqVO.getActiveTime()) - .eqIfPresent(IotDeviceDO::getDeviceSecret, reqVO.getDeviceSecret()) - .eqIfPresent(IotDeviceDO::getMqttClientId, reqVO.getMqttClientId()) - .likeIfPresent(IotDeviceDO::getMqttUsername, reqVO.getMqttUsername()) - .eqIfPresent(IotDeviceDO::getMqttPassword, reqVO.getMqttPassword()) - .eqIfPresent(IotDeviceDO::getAuthType, reqVO.getAuthType()) - .betweenIfPresent(IotDeviceDO::getCreateTime, reqVO.getCreateTime()) + .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0") .orderByDesc(IotDeviceDO::getId)); } From b143bc177f8fccb778eb60a2f470cc99f33a77a2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 17:00:58 +0800 Subject: [PATCH 021/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E5=88=86?= =?UTF-8?q?=E7=BB=84=E7=9A=84=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 3 + .../vo/group/IotDeviceGroupPageReqVO.java | 27 +++++++ .../device/vo/group/IotDeviceGroupRespVO.java | 27 +++++++ .../vo/group/IotDeviceGroupSaveReqVO.java | 26 +++++++ .../dataobject/device/IotDeviceGroupDO.java | 44 ++++++++++++ .../mysql/device/IotDeviceGroupMapper.java | 25 +++++++ .../service/device/IotDeviceGroupService.java | 54 ++++++++++++++ .../device/IotDeviceGroupServiceImpl.java | 70 +++++++++++++++++++ 8 files changed, 276 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 0d8b7d8bcc..55e560757a 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -33,4 +33,7 @@ public interface ErrorCodeConstants { // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); + // ========== 设备分组 1-050-005-000 ========== + ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java new file mode 100644 index 0000000000..1490f2894e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 设备分组分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class IotDeviceGroupPageReqVO extends PageParam { + + @Schema(description = "分组名字", example = "李四") + private String name; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java new file mode 100644 index 0000000000..50306cb766 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - IoT 设备分组 Response VO") +@Data +public class IotDeviceGroupRespVO { + + @Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583") + private Long id; + + @Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + private String name; + + @Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "分组描述", example = "你说的对") + private String description; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java new file mode 100644 index 0000000000..491cd9366b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupSaveReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.group; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 设备分组新增/修改 Request VO") +@Data +public class IotDeviceGroupSaveReqVO { + + @Schema(description = "分组 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3583") + private Long id; + + @Schema(description = "分组名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotEmpty(message = "分组名字不能为空") + private String name; + + @Schema(description = "分组状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分组状态不能为空") + private Integer status; + + @Schema(description = "分组描述", example = "你说的对") + private String description; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java new file mode 100644 index 0000000000..44c471216d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.device; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * IoT 设备分组 DO + * + * @author 芋道源码 + */ +@TableName("iot_device_group") +@KeySequence("iot_device_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotDeviceGroupDO extends BaseDO { + + /** + * 分组 ID + */ + @TableId + private Long id; + /** + * 分组名字 + */ + private String name; + /** + * 分组状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 分组描述 + */ + private String description; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java new file mode 100644 index 0000000000..492f7ee3f4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.device; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * IoT 设备分组 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotDeviceGroupMapper extends BaseMapperX { + + default PageResult selectPage(IotDeviceGroupPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotDeviceGroupDO::getName, reqVO.getName()) + .betweenIfPresent(IotDeviceGroupDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(IotDeviceGroupDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java new file mode 100644 index 0000000000..f4966b18f9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; +import jakarta.validation.Valid; + +/** + * IoT 设备分组 Service 接口 + * + * @author 芋道源码 + */ +public interface IotDeviceGroupService { + + /** + * 创建IoT 设备分组 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO); + + /** + * 更新IoT 设备分组 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO); + + /** + * 删除IoT 设备分组 + * + * @param id 编号 + */ + void deleteDeviceGroup(Long id); + + /** + * 获得IoT 设备分组 + * + * @param id 编号 + * @return IoT 设备分组 + */ + IotDeviceGroupDO getDeviceGroup(Long id); + + /** + * 获得IoT 设备分组分页 + * + * @param pageReqVO 分页查询 + * @return IoT 设备分组分页 + */ + PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java new file mode 100644 index 0000000000..957775c5e0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; +import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceGroupMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS; + +/** + * IoT 设备分组 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class IotDeviceGroupServiceImpl implements IotDeviceGroupService { + + @Resource + private IotDeviceGroupMapper deviceGroupMapper; + + @Override + public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) { + // 插入 + IotDeviceGroupDO deviceGroup = BeanUtils.toBean(createReqVO, IotDeviceGroupDO.class); + deviceGroupMapper.insert(deviceGroup); + // 返回 + return deviceGroup.getId(); + } + + @Override + public void updateDeviceGroup(IotDeviceGroupSaveReqVO updateReqVO) { + // 校验存在 + validateDeviceGroupExists(updateReqVO.getId()); + // 更新 + IotDeviceGroupDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceGroupDO.class); + deviceGroupMapper.updateById(updateObj); + } + + @Override + public void deleteDeviceGroup(Long id) { + // 校验存在 + validateDeviceGroupExists(id); + // 删除 + deviceGroupMapper.deleteById(id); + } + + private void validateDeviceGroupExists(Long id) { + if (deviceGroupMapper.selectById(id) == null) { + throw exception(DEVICE_GROUP_NOT_EXISTS); + } + } + + @Override + public IotDeviceGroupDO getDeviceGroup(Long id) { + return deviceGroupMapper.selectById(id); + } + + @Override + public PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) { + return deviceGroupMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file From b5ac5261394db19b535fdbe18ee1aac2a892baae Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 18:41:46 +0800 Subject: [PATCH 022/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=95=8C=E9=9D=A2=E5=A2=9E=E5=8A=A0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=88=86=E7=BB=84=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 2 +- .../device/IotDeviceGroupController.java | 88 +++++++++++++++++++ .../device/vo/device/IotDeviceRespVO.java | 4 + .../device/vo/device/IotDeviceSaveReqVO.java | 5 ++ .../device/vo/group/IotDeviceGroupRespVO.java | 3 + .../dal/dataobject/device/IotDeviceDO.java | 2 +- .../mysql/device/IotDeviceGroupMapper.java | 6 ++ .../iot/dal/mysql/device/IotDeviceMapper.java | 6 ++ .../service/device/IotDeviceGroupService.java | 48 ++++++++-- .../device/IotDeviceGroupServiceImpl.java | 25 +++++- .../iot/service/device/IotDeviceService.java | 8 ++ .../service/device/IotDeviceServiceImpl.java | 13 +++ 12 files changed, 198 insertions(+), 12 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 55e560757a..b75effb105 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -35,5 +35,5 @@ public interface ErrorCodeConstants { // ========== 设备分组 1-050-005-000 ========== ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); - + ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java new file mode 100644 index 0000000000..d19cf7fc9f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceGroupController.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceGroupService; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - IoT 设备分组") +@RestController +@RequestMapping("/iot/device-group") +@Validated +public class IotDeviceGroupController { + + @Resource + private IotDeviceGroupService deviceGroupService; + @Resource + private IotDeviceService deviceService; + + @PostMapping("/create") + @Operation(summary = "创建设备分组") + @PreAuthorize("@ss.hasPermission('iot:device-group:create')") + public CommonResult createDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO createReqVO) { + return success(deviceGroupService.createDeviceGroup(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新设备分组") + @PreAuthorize("@ss.hasPermission('iot:device-group:update')") + public CommonResult updateDeviceGroup(@Valid @RequestBody IotDeviceGroupSaveReqVO updateReqVO) { + deviceGroupService.updateDeviceGroup(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除设备分组") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:device-group:delete')") + public CommonResult deleteDeviceGroup(@RequestParam("id") Long id) { + deviceGroupService.deleteDeviceGroup(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得设备分组") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:device-group:query')") + public CommonResult getDeviceGroup(@RequestParam("id") Long id) { + IotDeviceGroupDO deviceGroup = deviceGroupService.getDeviceGroup(id); + return success(BeanUtils.toBean(deviceGroup, IotDeviceGroupRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得设备分组分页") + @PreAuthorize("@ss.hasPermission('iot:device-group:query')") + public CommonResult> getDeviceGroupPage(@Valid IotDeviceGroupPageReqVO pageReqVO) { + PageResult pageResult = deviceGroupService.getDeviceGroupPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotDeviceGroupRespVO.class, + group -> group.setDeviceCount(deviceService.getDeviceCountByGroupId(group.getId())))); + } + + @GetMapping("/simple-list") + @Operation(summary = "获取设备分组的精简信息列表", description = "只包含被开启的分组,主要用于前端的下拉选项") + public CommonResult> getSimpleDeviceGroupList() { + List list = deviceGroupService.getDeviceGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, group -> // 只返回 id、name 字段 + new IotDeviceGroupRespVO().setId(group.getId()).setName(group.getName()))); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java index fd2fbaa686..9214adaf2d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java @@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.Set; @Schema(description = "管理后台 - IoT 设备 Response VO") @Data @@ -23,6 +24,9 @@ public class IotDeviceRespVO { @ExcelProperty("设备名称备") private String deviceName; + @Schema(description = "设备分组编号数组", example = "1,2") + private Set groupIds; + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") @ExcelProperty("产品编号") private Long productId; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index e4f23d97e2..97220d57a2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.util.Set; + @Schema(description = "管理后台 - IoT 设备新增/修改 Request VO") @Data public class IotDeviceSaveReqVO { @@ -25,6 +27,9 @@ public class IotDeviceSaveReqVO { @Schema(description = "设备图片", example = "https://iocoder.cn/1.png") private String picUrl; + @Schema(description = "设备分组编号数组", example = "1,2") + private Set groupIds; + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") private Long productId; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java index 50306cb766..4fd5415028 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupRespVO.java @@ -24,4 +24,7 @@ public class IotDeviceGroupRespVO { @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; + @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long deviceCount; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index 6028c25fe0..f396855f13 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -57,7 +57,7 @@ public class IotDeviceDO extends BaseDO { /** * 设备分组编号集合 * - * 关联 TODO 芋艿: + * 关联 {@link IotDeviceGroupDO#getId()} */ @TableField(typeHandler = LongSetTypeHandler.class) private Set groupIds; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java index 492f7ee3f4..605ce5270a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGro import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * IoT 设备分组 Mapper * @@ -22,4 +24,8 @@ public interface IotDeviceGroupMapper extends BaseMapperX { .orderByDesc(IotDeviceGroupDO::getId)); } + default List selectListByStatus(Integer status) { + return selectList(IotDeviceGroupDO::getStatus, status); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 0459c964d9..59794c4edf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -50,4 +50,10 @@ public interface IotDeviceMapper extends BaseMapperX { return selectList(IotDeviceDO::getDeviceType, deviceType); } + default Long selectCountByGroupId(Long groupId) { + return selectCount(new LambdaQueryWrapperX() + .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0") + .orderByDesc(IotDeviceDO::getId)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java index f4966b18f9..5219f2d4bd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java @@ -1,11 +1,17 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + /** * IoT 设备分组 Service 接口 * @@ -14,7 +20,7 @@ import jakarta.validation.Valid; public interface IotDeviceGroupService { /** - * 创建IoT 设备分组 + * 创建设备分组 * * @param createReqVO 创建信息 * @return 编号 @@ -22,33 +28,61 @@ public interface IotDeviceGroupService { Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO); /** - * 更新IoT 设备分组 + * 更新设备分组 * * @param updateReqVO 更新信息 */ void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO); /** - * 删除IoT 设备分组 + * 删除设备分组 * * @param id 编号 */ void deleteDeviceGroup(Long id); /** - * 获得IoT 设备分组 + * 校验设备分组是否存在 + * + * @param ids 设备分组 ID 数组 + */ + default List validateDeviceGroupExists(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return List.of(); + } + return convertList(ids, this::validateDeviceGroupExists); + } + + /** + * 校验设备分组是否存在 + * + * @param id 设备分组 ID + * @return 设备分组 + */ + IotDeviceGroupDO validateDeviceGroupExists(Long id); + + /** + * 获得设备分组 * * @param id 编号 - * @return IoT 设备分组 + * @return 设备分组 */ IotDeviceGroupDO getDeviceGroup(Long id); /** - * 获得IoT 设备分组分页 + * 获得设备分组分页 * * @param pageReqVO 分页查询 - * @return IoT 设备分组分页 + * @return 设备分组分页 */ PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO); + /** + * 获得设备分组列表 + * + * @param status 状态 + * @return 设备分组列表 + */ + List getDeviceGroupListByStatus(Integer status); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java index 957775c5e0..cfa24784b7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java @@ -10,7 +10,10 @@ import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.List; + import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS; /** @@ -25,6 +28,9 @@ public class IotDeviceGroupServiceImpl implements IotDeviceGroupService { @Resource private IotDeviceGroupMapper deviceGroupMapper; + @Resource + private IotDeviceService deviceService; + @Override public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) { // 插入 @@ -45,16 +51,24 @@ public class IotDeviceGroupServiceImpl implements IotDeviceGroupService { @Override public void deleteDeviceGroup(Long id) { - // 校验存在 + // 1.1 校验存在 validateDeviceGroupExists(id); + // 1.2 校验是否存在设备 + if (deviceService.getDeviceCountByGroupId(id) > 0) { + throw exception(DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS); + } + // 删除 deviceGroupMapper.deleteById(id); } - private void validateDeviceGroupExists(Long id) { - if (deviceGroupMapper.selectById(id) == null) { + @Override + public IotDeviceGroupDO validateDeviceGroupExists(Long id) { + IotDeviceGroupDO group = deviceGroupMapper.selectById(id); + if (group == null) { throw exception(DEVICE_GROUP_NOT_EXISTS); } + return group; } @Override @@ -67,4 +81,9 @@ public class IotDeviceGroupServiceImpl implements IotDeviceGroupService { return deviceGroupMapper.selectPage(pageReqVO); } + @Override + public List getDeviceGroupListByStatus(Integer status) { + return deviceGroupMapper.selectListByStatus(status); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 3569c75977..8ae81e6119 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -78,6 +78,14 @@ public interface IotDeviceService { */ Long getDeviceCountByProductId(Long productId); + /** + * 获得设备数量 + * + * @param groupId 分组编号 + * @return 设备数量 + */ + Long getDeviceCountByGroupId(Long groupId); + /** * 根据产品 key 和设备名称,获得设备信息 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index c882b7420a..482dc606e5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -42,6 +43,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Resource private IotProductService productService; + @Resource + @Lazy // 延迟加载,解决循环依赖 + private IotDeviceGroupService deviceGroupService; @Override public Long createDevice(IotDeviceSaveReqVO createReqVO) { @@ -63,6 +67,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { && createReqVO.getGatewayId() != null) { validateGatewayDeviceExists(createReqVO.getGatewayId()); } + // 1.5 校验分组存在 + deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); // 2.1 转换 VO 为 DO IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> { @@ -90,6 +96,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { && updateReqVO.getGatewayId() != null) { validateGatewayDeviceExists(updateReqVO.getGatewayId()); } + // 1.3 校验分组存在 + deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds()); // 2. 更新到数据库 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); @@ -182,6 +190,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectCountByProductId(productId); } + @Override + public Long getDeviceCountByGroupId(Long groupId) { + return deviceMapper.selectCountByGroupId(groupId); + } + @Override @TenantIgnore public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) { From 345065815945b5512a7a87bb3becd99e6229e99d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 18:56:51 +0800 Subject: [PATCH 023/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=95=8C=E9=9D=A2=E5=A2=9E=E5=8A=A0=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/DictTypeConstants.java | 3 +++ .../admin/device/IotDeviceController.java | 19 ++++++++++++++ .../device/vo/device/IotDeviceRespVO.java | 25 ++++++++++++++----- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java index b5c34f14b7..c8e6c70c1f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -13,4 +13,7 @@ public class DictTypeConstants { public static final String PROTOCOL_TYPE = "iot_protocol_type"; public static final String DATA_FORMAT = "iot_data_format"; public static final String VALIDATE_TYPE = "iot_validate_type"; + + public static final String DEVICE_STATUS = "iot_device_status"; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 2dee57d6d9..affdd0a4d9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceRespVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; @@ -13,13 +16,16 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.io.IOException; import java.util.List; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -81,6 +87,19 @@ public class IotDeviceController { return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class)); } + @GetMapping("/export-excel") + @Operation(summary = "导出设备 Excel") + @PreAuthorize("@ss.hasPermission('iot:device:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDeviceExcel(@Valid IotDevicePageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + CommonResult> result = getDevicePage(exportReqVO); + // 导出 Excel + ExcelUtils.write(response, "设备.xls", "数据", IotDeviceRespVO.class, + result.getData().getList()); + } + @GetMapping("/count") @Operation(summary = "获得设备数量") @Parameter(name = "productId", description = "产品编号", example = "1") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java index 9214adaf2d..1516637590 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -8,6 +10,8 @@ import lombok.Data; import java.time.LocalDateTime; import java.util.Set; +import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.DEVICE_STATUS; + @Schema(description = "管理后台 - IoT 设备 Response VO") @Data @ExcelIgnoreUnannotated @@ -21,9 +25,21 @@ public class IotDeviceRespVO { private String deviceKey; @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - @ExcelProperty("设备名称备") + @ExcelProperty("设备名称") private String deviceName; + @Schema(description = "设备备注名称", example = "张三") + @ExcelProperty("设备备注名称") + private String nickname; + + @Schema(description = "设备序列号", example = "1024") + @ExcelProperty("设备序列号") + private String serialNumber; + + @Schema(description = "设备图片", example = "我是一名码农") + @ExcelProperty("设备图片") + private String picUrl; + @Schema(description = "设备分组编号数组", example = "1,2") private Set groupIds; @@ -39,15 +55,12 @@ public class IotDeviceRespVO { @ExcelProperty("设备类型") private Integer deviceType; - @Schema(description = "设备备注名称", example = "张三") - @ExcelProperty("设备备注名称") - private String nickname; - @Schema(description = "网关设备 ID", example = "16380") private Long gatewayId; @Schema(description = "设备状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("设备状态") + @ExcelProperty(value = "设备状态", converter = DictConvert.class) + @DictFormat(DEVICE_STATUS) private Integer status; @Schema(description = "设备状态最后更新时间") From b02e396aff2885e402f598d300ad923d559396fd Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 19:12:42 +0800 Subject: [PATCH 024/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=95=8C=E9=9D=A2=E5=A2=9E=E5=8A=A0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceController.java | 12 ++++++++- .../iot/service/device/IotDeviceService.java | 10 +++++++- .../service/device/IotDeviceServiceImpl.java | 25 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index affdd0a4d9..417df29841 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -23,6 +23,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.io.IOException; +import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; @@ -62,7 +63,7 @@ public class IotDeviceController { } @DeleteMapping("/delete") - @Operation(summary = "删除设备") + @Operation(summary = "删除单个设备") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('iot:device:delete')") public CommonResult deleteDevice(@RequestParam("id") Long id) { @@ -70,6 +71,15 @@ public class IotDeviceController { return success(true); } + @DeleteMapping("/delete-list") + @Operation(summary = "删除多个设备") + @Parameter(name = "ids", description = "编号数组", required = true) + @PreAuthorize("@ss.hasPermission('iot:device:delete')") + public CommonResult deleteDeviceList(@RequestParam("ids") Collection ids) { + deviceService.deleteDeviceList(ids); + return success(true); + } + @GetMapping("/get") @Operation(summary = "获得设备") @Parameter(name = "id", description = "编号", required = true, example = "1024") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 8ae81e6119..a76f09486b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import jakarta.validation.Valid; import javax.annotation.Nullable; +import java.util.Collection; import java.util.List; /** @@ -33,12 +34,19 @@ public interface IotDeviceService { void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); /** - * 删除设备 + * 删除单个设备 * * @param id 编号 */ void deleteDevice(Long id); + /** + * 删除多个设备 + * + * @param ids 编号数组 + */ + void deleteDeviceList(Collection ids); + /** * 获得设备 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 482dc606e5..3af71593b4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -18,10 +19,12 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Nullable; import java.time.LocalDateTime; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -117,6 +120,28 @@ public class IotDeviceServiceImpl implements IotDeviceService { deviceMapper.deleteById(id); } + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDeviceList(Collection ids) { + // 1.1 校验存在 + if (CollUtil.isEmpty(ids)) { + return; + } + List devices = deviceMapper.selectBatchIds(ids); + if (CollUtil.isEmpty(devices)) { + return; + } + // 1.2 校验网关设备是否存在 + for (IotDeviceDO device : devices) { + if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(device.getId()) > 0) { + throw exception(DEVICE_HAS_CHILDREN); + } + } + + // 2. 删除设备 + deviceMapper.deleteByIds(ids); + } + /** * 校验设备是否存在 * From 39ba4e72da281a90715baab359a8995627747f7a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 14 Dec 2024 19:43:22 +0800 Subject: [PATCH 025/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=95=8C=E9=9D=A2=E5=A2=9E=E5=8A=A0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=88=86=E7=BB=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceController.java | 17 +++++++++----- .../vo/device/IotDeviceUpdateGroupReqVO.java | 21 ++++++++++++++++++ .../iot/service/device/IotDeviceService.java | 22 +++++++++++++------ .../service/device/IotDeviceServiceImpl.java | 20 ++++++++++++++++- 4 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 417df29841..35b4abe937 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -6,10 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import io.swagger.v3.oas.annotations.Operation; @@ -62,6 +59,14 @@ public class IotDeviceController { return success(true); } + @PutMapping("/update-group") + @Operation(summary = "更新设备分组") + @PreAuthorize("@ss.hasPermission('iot:device:update')") + public CommonResult updateDeviceGroup(@Valid @RequestBody IotDeviceUpdateGroupReqVO updateReqVO) { + deviceService.updateDeviceGroup(updateReqVO); + return success(true); + } + @DeleteMapping("/delete") @Operation(summary = "删除单个设备") @Parameter(name = "id", description = "编号", required = true) @@ -102,7 +107,7 @@ public class IotDeviceController { @PreAuthorize("@ss.hasPermission('iot:device:export')") @ApiAccessLog(operateType = EXPORT) public void exportDeviceExcel(@Valid IotDevicePageReqVO exportReqVO, - HttpServletResponse response) throws IOException { + HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); CommonResult> result = getDevicePage(exportReqVO); // 导出 Excel @@ -125,7 +130,7 @@ public class IotDeviceController { @RequestParam(value = "deviceType", required = false) Integer deviceType) { List list = deviceService.getDeviceList(deviceType); return success(convertList(list, device -> // 只返回 id、name 字段 - new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); + new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java new file mode 100644 index 0000000000..bf66fbf98f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceUpdateGroupReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Set; + +@Schema(description = "管理后台 - IoT 设备更新分组 Request VO") +@Data +public class IotDeviceUpdateGroupReqVO { + + @Schema(description = "设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + @NotEmpty(message = "设备编号列表不能为空") + private Set ids; + + @Schema(description = "分组编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + @NotEmpty(message = "分组编号列表不能为空") + private Set groupIds; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index a76f09486b..42101626c0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import jakarta.validation.Valid; @@ -33,6 +34,20 @@ public interface IotDeviceService { */ void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); + /** + * 更新设备状态 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO); + + /** + * 更新设备分组 + * + * @param updateReqVO 更新信息 + */ + void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO); + /** * 删除单个设备 * @@ -71,13 +86,6 @@ public interface IotDeviceService { */ List getDeviceList(@Nullable Integer deviceType); - /** - * 更新设备状态 - * - * @param updateReqVO 更新信息 - */ - void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO); - /** * 获得设备数量 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 3af71593b4..29944eae7b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; @@ -29,6 +30,7 @@ import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; /** @@ -67,7 +69,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { } // 1.4 校验父设备是否为合法网关 if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType()) - && createReqVO.getGatewayId() != null) { + && createReqVO.getGatewayId() != null) { validateGatewayDeviceExists(createReqVO.getGatewayId()); } // 1.5 校验分组存在 @@ -107,6 +109,22 @@ public class IotDeviceServiceImpl implements IotDeviceService { deviceMapper.updateById(updateObj); } + @Override + @Transactional(rollbackFor = Exception.class) + public void updateDeviceGroup(IotDeviceUpdateGroupReqVO updateReqVO) { + // 1.1 校验设备存在 + List devices = deviceMapper.selectBatchIds(updateReqVO.getIds()); + if (CollUtil.isEmpty(devices)) { + return; + } + // 1.2 校验分组存在 + deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds()); + + // 3. 更新设备分组 + deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO() + .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds()))); + } + @Override public void deleteDevice(Long id) { // 1.1 校验存在 From 555310de66d18250116f14d2cf93f42155cc3862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 14 Dec 2024 21:51:17 +0800 Subject: [PATCH 026/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E6=8F=92=E4=BB=B6=E5=AE=9E=E4=BE=8B=E5=92=8C=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=9A=84=E5=AE=9A=E4=B9=89=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 8 + yudao-module-iot/pom.xml | 1 + .../module/iot/enums/ErrorCodeConstants.java | 13 +- .../enums/plugin/IotPluginDeployTypeEnum.java | 53 ++++ .../iot/enums/plugin/IotPluginStatusEnum.java | 57 ++++ .../iot/enums/plugin/IotPluginTypeEnum.java | 53 ++++ yudao-module-iot/yudao-module-iot-biz/pom.xml | 6 + .../plugininfo/PluginInfoController.java | 98 +++++++ .../plugininfo/vo/PluginInfoPageReqVO.java | 57 ++++ .../admin/plugininfo/vo/PluginInfoRespVO.java | 70 +++++ .../plugininfo/vo/PluginInfoSaveReqVO.java | 49 ++++ .../PluginInstanceController.java | 94 ++++++ .../vo/PluginInstancePageReqVO.java | 36 +++ .../vo/PluginInstanceRespVO.java | 43 +++ .../vo/PluginInstanceSaveReqVO.java | 35 +++ .../dataobject/plugininfo/PluginInfoDO.java | 78 +++++ .../plugininstance/PluginInstanceDO.java | 51 ++++ .../mysql/plugininfo/PluginInfoMapper.java | 36 +++ .../plugininstance/PluginInstanceMapper.java | 29 ++ .../framework/plugin/SpringConfiguration.java | 15 + .../service/plugininfo/PluginInfoService.java | 72 +++++ .../plugininfo/PluginInfoServiceImpl.java | 267 ++++++++++++++++++ .../plugininstance/PluginInstanceService.java | 54 ++++ .../PluginInstanceServiceImpl.java | 70 +++++ .../mapper/plugininfo/PluginInfoMapper.xml | 12 + .../plugininstance/PluginInstanceMapper.xml | 12 + .../plugininfo/PluginInfoServiceImplTest.java | 171 +++++++++++ .../PluginInstanceServiceImplTest.java | 150 ++++++++++ .../yudao-module-iot-plugin/pom.xml | 31 ++ .../yudao/module/iot/plugin/package-info.java | 6 + 30 files changed, 1726 insertions(+), 1 deletion(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/pom.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 486fe124b3..6eaa89dfe7 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -66,6 +66,7 @@ 2.7.0 3.0.6 1.2.5 + 0.9.0 3.5.0 4.11.0 @@ -605,6 +606,13 @@ ${mqtt.version} + + + org.pf4j + pf4j-spring + ${pf4j-spring.version} + + diff --git a/yudao-module-iot/pom.xml b/yudao-module-iot/pom.xml index 069af1699b..d9002abea5 100644 --- a/yudao-module-iot/pom.xml +++ b/yudao-module-iot/pom.xml @@ -10,6 +10,7 @@ yudao-module-iot-api yudao-module-iot-biz + yudao-module-iot-plugin 4.0.0 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index b75effb105..5f58109b36 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -36,4 +36,15 @@ public interface ErrorCodeConstants { // ========== 设备分组 1-050-005-000 ========== ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); -} + + // ========== 插件信息 1-050-006-000 ========== + ErrorCode PLUGIN_INFO_NOT_EXISTS = new ErrorCode(1_050_006_000, "插件信息不存在"); + ErrorCode PLUGIN_INSTALL_FAILED = new ErrorCode(1_050_006_001, "插件安装失败"); + ErrorCode PLUGIN_INSTALL_FAILED_FILE_NAME_NOT_MATCH = new ErrorCode(1_050_006_002, "插件安装失败,文件名与原插件id不匹配"); + ErrorCode PLUGIN_INFO_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件"); + ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效"); + + // ========== 插件实例 1-050-007-000 ========== + ErrorCode PLUGIN_INSTANCE_NOT_EXISTS = new ErrorCode(1_050_007_000, "插件实例不存在"); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java new file mode 100644 index 0000000000..fd15514c46 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.iot.enums.plugin; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; + +import java.util.Arrays; + +/** + * IoT 部署方式枚举 + * + * @author haohao + */ +@Getter +public enum IotPluginDeployTypeEnum implements IntArrayValuable { + + UPLOAD(0, "上传jar"), + ALONE(1, "独立运行"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray(); + + /** + * 部署方式 + */ + private final Integer deployType; + + /** + * 部署方式名 + */ + private final String name; + + IotPluginDeployTypeEnum(Integer deployType, String name) { + this.deployType = deployType; + this.name = name; + } + + public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) { + for (IotPluginDeployTypeEnum value : values()) { + if (value.getDeployType().equals(deployType)) { + return value; + } + } + return null; + } + + public static boolean isValidDeployType(Integer deployType) { + return fromDeployType(deployType) != null; + } + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java new file mode 100644 index 0000000000..25b1720225 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.enums.plugin; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; + +import java.util.Arrays; + +/** + * IoT 插件状态枚举 + * + * @author haohao + */ +@Getter +public enum IotPluginStatusEnum implements IntArrayValuable { + + STOPPED(0, "停止"), + RUNNING(1, "运行"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginStatusEnum::getStatus).toArray(); + + /** + * 状态 + */ + private final Integer status; + + /** + * 状态名 + */ + private final String name; + + IotPluginStatusEnum(Integer status, String name) { + this.status = status; + this.name = name; + } + + public static IotPluginStatusEnum fromState(Integer state) { + for (IotPluginStatusEnum value : values()) { + if (value.getStatus().equals(state)) { + return value; + } + } + return null; + } + + public static boolean isValidState(Integer state) { + return fromState(state) != null; + } + + public static boolean contains(Integer status) { + return Arrays.stream(values()).anyMatch(e -> e.getStatus().equals(status)); + } + + @Override + public int[] array() { + return new int[0]; + } +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java new file mode 100644 index 0000000000..9e1e5d7f05 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.iot.enums.plugin; + +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.Getter; + +import java.util.Arrays; + +/** + * IoT 插件类型枚举 + * + * @author haohao + */ +@Getter +public enum IotPluginTypeEnum implements IntArrayValuable { + + NORMAL(0, "普通插件"), + DEVICE(1, "设备插件"); + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginTypeEnum::getType).toArray(); + + /** + * 类型 + */ + private final Integer type; + + /** + * 类型名 + */ + private final String name; + + IotPluginTypeEnum(Integer type, String name) { + this.type = type; + this.name = name; + } + + public static IotPluginTypeEnum fromType(Integer type) { + for (IotPluginTypeEnum value : values()) { + if (value.getType().equals(type)) { + return value; + } + } + return null; + } + + public static boolean isValidType(Integer type) { + return fromType(type) != null; + } + + @Override + public int[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 013287d2b9..b003e1785a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -69,6 +69,12 @@ org.eclipse.paho org.eclipse.paho.client.mqttv3 + + + + org.pf4j + pf4j-spring + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java new file mode 100644 index 0000000000..d87a14590b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java @@ -0,0 +1,98 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY; + +@Tag(name = "管理后台 - IoT 插件信息") +@RestController +@RequestMapping("/iot/plugin-info") +@Validated +public class PluginInfoController { + + @Resource + private PluginInfoService pluginInfoService; + + @PostMapping("/create") + @Operation(summary = "创建插件信息") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:create')") + public CommonResult createPluginInfo(@Valid @RequestBody PluginInfoSaveReqVO createReqVO) { + return success(pluginInfoService.createPluginInfo(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新插件信息") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") + public CommonResult updatePluginInfo(@Valid @RequestBody PluginInfoSaveReqVO updateReqVO) { + pluginInfoService.updatePluginInfo(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除插件信息") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:plugin-info:delete')") + public CommonResult deletePluginInfo(@RequestParam("id") Long id) { + pluginInfoService.deletePluginInfo(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得插件信息") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')") + public CommonResult getPluginInfo(@RequestParam("id") Long id) { + PluginInfoDO pluginInfo = pluginInfoService.getPluginInfo(id); + return success(BeanUtils.toBean(pluginInfo, PluginInfoRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得插件信息分页") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')") + public CommonResult> getPluginInfoPage(@Valid PluginInfoPageReqVO pageReqVO) { + PageResult pageResult = pluginInfoService.getPluginInfoPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class)); + } + + @RequestMapping(value = "/update-jar", + method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题 + @Operation(summary = "上传Jar包") + public CommonResult uploadJar( + @RequestParam("id") Long id, + @RequestParam("jar") MultipartFile file) throws Exception { + if (file.isEmpty()) { + throw exception(FILE_IS_EMPTY); + } + pluginInfoService.uploadJar(id, file); + return success(true); + } + + // 修改插件状态 + @PutMapping("/update-status") + @Operation(summary = "修改插件状态") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") + public CommonResult updateUserStatus(@Valid @RequestBody PluginInfoSaveReqVO reqVO) { + pluginInfoService.updatePluginStatus(reqVO.getId(), reqVO.getStatus()); + return success(true); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java new file mode 100644 index 0000000000..1ddb8e1ffc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PluginInfoPageReqVO extends PageParam { + + @Schema(description = "插件包id", example = "24627") + private String pluginId; + + @Schema(description = "插件名称", example = "赵六") + private String name; + + @Schema(description = "描述", example = "你猜") + private String description; + + @Schema(description = "部署方式", example = "2") + private Integer deployType; + + @Schema(description = "插件包文件名") + private String file; + + @Schema(description = "插件版本") + private String version; + + @Schema(description = "插件类型", example = "2") + private Integer type; + + @Schema(description = "设备插件协议类型") + private String protocol; + + @Schema(description = "状态") + private Integer status; + + @Schema(description = "插件配置项描述信息") + private String configSchema; + + @Schema(description = "插件配置信息") + private String config; + + @Schema(description = "插件脚本") + private String script; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java new file mode 100644 index 0000000000..1e9f2b7dd6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - IoT 插件信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class PluginInfoRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + @ExcelProperty("主键ID") + private Long id; + + @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") + @ExcelProperty("插件包id") + private String pluginId; + + @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @ExcelProperty("插件名称") + private String name; + + @Schema(description = "描述", example = "你猜") + @ExcelProperty("描述") + private String description; + + @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("部署方式") + private Integer deployType; + + @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("插件包文件名") + private String file; + + @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("插件版本") + private String version; + + @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("插件类型") + private Integer type; + + @Schema(description = "设备插件协议类型") + @ExcelProperty("设备插件协议类型") + private String protocol; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("状态") + private Integer status; + + @Schema(description = "插件配置项描述信息") + @ExcelProperty("插件配置项描述信息") + private String configSchema; + + @Schema(description = "插件配置信息") + @ExcelProperty("插件配置信息") + private String config; + + @Schema(description = "插件脚本") + @ExcelProperty("插件脚本") + private String script; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java new file mode 100644 index 0000000000..8ce254a3ae --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +@Schema(description = "管理后台 - IoT 插件信息新增/修改 Request VO") +@Data +public class PluginInfoSaveReqVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + private Long id; + + @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") + private String pluginId; + + @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + private String name; + + @Schema(description = "描述", example = "你猜") + private String description; + + @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer deployType; + + @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) + private String file; + + @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) + private String version; + + @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer type; + + @Schema(description = "设备插件协议类型") + private String protocol; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer status; + + @Schema(description = "插件配置项描述信息") + private String configSchema; + + @Schema(description = "插件配置信息") + private String config; + + @Schema(description = "插件脚本") + private String script; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java new file mode 100644 index 0000000000..9382f8c6d7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance; + +import org.springframework.web.bind.annotation.*; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import jakarta.validation.*; +import jakarta.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService; + +@Tag(name = "管理后台 - IoT 插件实例") +@RestController +@RequestMapping("/iot/plugin-instance") +@Validated +public class PluginInstanceController { + + @Resource + private PluginInstanceService pluginInstanceService; + + @PostMapping("/create") + @Operation(summary = "创建IoT 插件实例") + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')") + public CommonResult createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) { + return success(pluginInstanceService.createPluginInstance(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新IoT 插件实例") + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')") + public CommonResult updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) { + pluginInstanceService.updatePluginInstance(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除IoT 插件实例") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')") + public CommonResult deletePluginInstance(@RequestParam("id") Long id) { + pluginInstanceService.deletePluginInstance(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得IoT 插件实例") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')") + public CommonResult getPluginInstance(@RequestParam("id") Long id) { + PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id); + return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得IoT 插件实例分页") + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')") + public CommonResult> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) { + PageResult pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出IoT 插件实例 Excel") + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class, + BeanUtils.toBean(list, PluginInstanceRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java new file mode 100644 index 0000000000..2e678fa9dd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 插件实例分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PluginInstancePageReqVO extends PageParam { + + @Schema(description = "插件主程序id", example = "23738") + private String mainId; + + @Schema(description = "插件id", example = "26498") + private Long pluginId; + + @Schema(description = "插件主程序所在ip") + private String ip; + + @Schema(description = "插件主程序端口") + private Integer port; + + @Schema(description = "心跳时间,心路时间超过30秒需要剔除") + private Long heartbeatAt; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java new file mode 100644 index 0000000000..e71ff29045 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - IoT 插件实例 Response VO") +@Data +@ExcelIgnoreUnannotated +public class PluginInstanceRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864") + @ExcelProperty("主键ID") + private Long id; + + @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738") + @ExcelProperty("插件主程序id") + private String mainId; + + @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498") + @ExcelProperty("插件id") + private Long pluginId; + + @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("插件主程序所在ip") + private String ip; + + @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("插件主程序端口") + private Integer port; + + @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("心跳时间,心路时间超过30秒需要剔除") + private Long heartbeatAt; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java new file mode 100644 index 0000000000..8d927045d5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import jakarta.validation.constraints.*; + +@Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO") +@Data +public class PluginInstanceSaveReqVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864") + private Long id; + + @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738") + @NotEmpty(message = "插件主程序id不能为空") + private String mainId; + + @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498") + @NotNull(message = "插件id不能为空") + private Long pluginId; + + @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "插件主程序所在ip不能为空") + private String ip; + + @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "插件主程序端口不能为空") + private Integer port; + + @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "心跳时间,心路时间超过30秒需要剔除不能为空") + private Long heartbeatAt; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java new file mode 100644 index 0000000000..07b2cc7ff9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * IoT 插件信息 DO + * + * @author 芋道源码 + */ +@TableName("iot_plugin_info") +@KeySequence("iot_plugin_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PluginInfoDO extends BaseDO { + + /** + * 主键ID + */ + @TableId + private Long id; + /** + * 插件包id + */ + private String pluginId; + /** + * 插件名称 + */ + private String name; + /** + * 描述 + */ + private String description; + /** + * 部署方式 + */ + private Integer deployType; + /** + * 插件包文件名 + */ + private String file; + /** + * 插件版本 + */ + private String version; + /** + * 插件类型 + */ + private Integer type; + /** + * 设备插件协议类型 + */ + private String protocol; + /** + * 状态 + */ + private Integer status; + /** + * 插件配置项描述信息 + */ + private String configSchema; + /** + * 插件配置信息 + */ + private String config; + /** + * 插件脚本 + */ + private String script; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java new file mode 100644 index 0000000000..79dd762c70 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * IoT 插件实例 DO + * + * @author 芋道源码 + */ +@TableName("iot_plugin_instance") +@KeySequence("iot_plugin_instance_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PluginInstanceDO extends BaseDO { + + /** + * 主键ID + */ + @TableId + private Long id; + /** + * 插件主程序id + */ + private String mainId; + /** + * 插件id + */ + private Long pluginId; + /** + * 插件主程序所在ip + */ + private String ip; + /** + * 插件主程序端口 + */ + private Integer port; + /** + * 心跳时间,心路时间超过30秒需要剔除 + */ + private Long heartbeatAt; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java new file mode 100644 index 0000000000..69c0bd3929 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*; + +/** + * IoT 插件信息 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface PluginInfoMapper extends BaseMapperX { + + default PageResult selectPage(PluginInfoPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId()) + .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) + .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription()) + .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType()) + .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile()) + .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion()) + .eqIfPresent(PluginInfoDO::getType, reqVO.getType()) + .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol()) + .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) + .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema()) + .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig()) + .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript()) + .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PluginInfoDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java new file mode 100644 index 0000000000..6c7f1d231c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; + +/** + * IoT 插件实例 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface PluginInstanceMapper extends BaseMapperX { + + default PageResult selectPage(PluginInstancePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId()) + .eqIfPresent(PluginInstanceDO::getPluginId, reqVO.getPluginId()) + .eqIfPresent(PluginInstanceDO::getIp, reqVO.getIp()) + .eqIfPresent(PluginInstanceDO::getPort, reqVO.getPort()) + .eqIfPresent(PluginInstanceDO::getHeartbeatAt, reqVO.getHeartbeatAt()) + .betweenIfPresent(PluginInstanceDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(PluginInstanceDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java new file mode 100644 index 0000000000..f43b69c12c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.iot.framework.plugin; + +import org.pf4j.spring.SpringPluginManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SpringConfiguration { + + @Bean + public SpringPluginManager pluginManager() { + return new SpringPluginManager(); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java new file mode 100644 index 0000000000..f9ae486772 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.iot.service.plugininfo; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import jakarta.validation.Valid; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; + +/** + * IoT 插件信息 Service 接口 + * + * @author 芋道源码 + */ +public interface PluginInfoService { + + /** + * 创建IoT 插件信息 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPluginInfo(@Valid PluginInfoSaveReqVO createReqVO); + + /** + * 更新IoT 插件信息 + * + * @param updateReqVO 更新信息 + */ + void updatePluginInfo(@Valid PluginInfoSaveReqVO updateReqVO); + + /** + * 删除IoT 插件信息 + * + * @param id 编号 + */ + void deletePluginInfo(Long id); + + /** + * 获得IoT 插件信息 + * + * @param id 编号 + * @return IoT 插件信息 + */ + PluginInfoDO getPluginInfo(Long id); + + /** + * 获得IoT 插件信息分页 + * + * @param pageReqVO 分页查询 + * @return IoT 插件信息分页 + */ + PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO); + + /** + * 上传插件的 JAR 包 + * + * @param id 插件id + * @param file 文件 + */ + void uploadJar(Long id, MultipartFile file); + + /** + * 更新插件的状态 + * + * @param id 插件id + * @param status 状态 + */ + void updatePluginStatus(Long id, Integer status); +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java new file mode 100644 index 0000000000..65ad6ad1db --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java @@ -0,0 +1,267 @@ +package cn.iocoder.yudao.module.iot.service.plugininfo; + +import cn.hutool.core.io.IoUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.infra.api.file.FileApi; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginDescriptor; +import org.pf4j.PluginState; +import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Path; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +/** + * IoT 插件信息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class PluginInfoServiceImpl implements PluginInfoService { + + @Resource + private PluginInfoMapper pluginInfoMapper; + + @Resource + private SpringPluginManager pluginManager; + + @Resource + private FileApi fileApi; + + @Override + public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { + // 插入 + PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class); + pluginInfoMapper.insert(pluginInfo); + // 返回 + return pluginInfo.getId(); + } + + @Override + public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) { + // 校验存在 + validatePluginInfoExists(updateReqVO.getId()); + // 更新 + PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class); + pluginInfoMapper.updateById(updateObj); + } + + @Override + public void deletePluginInfo(Long id) { + // 校验存在 + PluginInfoDO pluginInfoDO = validatePluginInfoExists(id); + + // 停止插件 + if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { + throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING); + } + + // 卸载插件 + PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId()); + if (plugin != null) { + // 查询插件是否是启动状态 + if (plugin.getPluginState().equals(PluginState.STARTED)) { + // 停止插件 + pluginManager.stopPlugin(plugin.getPluginId()); + } + // 卸载插件 + pluginManager.unloadPlugin(plugin.getPluginId()); + } + + // 删除 + pluginInfoMapper.deleteById(id); + } + + private PluginInfoDO validatePluginInfoExists(Long id) { + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id); + if (pluginInfo == null) { + throw exception(PLUGIN_INFO_NOT_EXISTS); + } + return pluginInfo; + } + + @Override + public PluginInfoDO getPluginInfo(Long id) { + return pluginInfoMapper.selectById(id); + } + + @Override + public PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO) { + return pluginInfoMapper.selectPage(pageReqVO); + } + + @Override + public void uploadJar(Long id, MultipartFile file) { + // 1. 校验存在 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); + + // 2. 判断文件名称与插件 ID 是否匹配 + String pluginId = pluginInfoDo.getPluginId(); + + // 3. 停止卸载旧的插件 + // 3.1. 获取插件信息 + PluginWrapper plugin = pluginManager.getPlugin(pluginId); + if (plugin != null) { + // 3.2. 如果插件状态是启动的,停止插件 + if (plugin.getPluginState().equals(PluginState.STARTED)) { + pluginManager.stopPlugin(pluginId); + } + // 3.3. 卸载插件 + pluginManager.unloadPlugin(pluginId); + } + + // 4. 上传插件 + String pluginIdNew; + try { + String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream())); + Path pluginPath = Path.of(path); + pluginIdNew = pluginManager.loadPlugin(pluginPath); + } catch (Exception e) { + throw exception(PLUGIN_INSTALL_FAILED); + } + + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew); + if (pluginWrapper == null) { + throw exception(PLUGIN_INSTALL_FAILED); + } + + + // 5. 读取配置文件和脚本 + String configJson = ""; + String script = ""; + + pluginInfoDo.setPluginId(pluginIdNew); + pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); + pluginInfoDo.setFile(file.getOriginalFilename()); + pluginInfoDo.setConfigSchema(configJson); + pluginInfoDo.setScript(script); + + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + pluginInfoDo.setVersion(pluginDescriptor.getVersion()); + pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); + pluginInfoMapper.updateById(pluginInfoDo); + + + // 5. 读取配置文件和脚本 +// String configJson = ""; + // String script = ""; +// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) { +// // 5.1 获取config文件在jar包中的路径 +// String configFile = "classes/config.json"; +// JarEntry configEntry = jarFile.getJarEntry(configFile); +// +// if (configEntry != null) { +// // 5.2 读取配置文件 +// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset()); +// log.info("configJson:{}", configJson); +// } +// +// // 5.3 读取script.js脚本 +// String scriptFile = "classes/script.js"; +// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile); +// if (scriptEntity != null) { +// // 5.4 读取脚本文件 +// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset()); +// log.info("script:{}", script); +// } +// } catch (Exception e) { +// throw exception(PLUGIN_INSTALL_FAILED); +// } + + +// PluginState pluginState = pluginInfoUpdate.getPluginState(); +// if (pluginState == PluginState.STARTED) { +// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus()); +// } +// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId()); +// pluginInfoDo.setFile(file.getOriginalFilename()); +// pluginInfoDo.setConfigSchema(configJson); +// pluginInfoDo.setScript(script); +// +// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor(); +// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion()); +// pluginInfoDo.setDescription(pluginDescriptor.getDescription()); +// pluginInfoMapper.updateById(pluginInfoDo); + } + + @Override + public void updatePluginStatus(Long id, Integer status) { + // 1. 校验存在 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); + + // 插件状态无效 + if (!IotPluginStatusEnum.contains(status)) { + throw exception(PLUGIN_STATUS_INVALID); + } + + // 插件包为空 +// String pluginId = pluginInfoDo.getPluginId(); +// if (StrUtil.isBlank(pluginId)) { +// throw exception(PLUGIN_INFO_NOT_EXISTS); +// } +// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId); +// if (pluginInfo != null) { +// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) { +// // 启动插件 +// pluginOperator.start(pluginId); +// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) { +// // 停止插件 +// pluginOperator.stop(pluginId); +// } +// } else { +// // 已经停止,未获取到插件 +// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { +// throw exception(PLUGIN_STATUS_INVALID); +// } +// } + pluginInfoDo.setStatus(status); + pluginInfoMapper.updateById(pluginInfoDo); + } + + @PostConstruct + public void init() { + Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS); + } + + @SneakyThrows + private void startPlugins() { +// while (!pluginOperator.inited()) { +// Thread.sleep(1000L); +// } + + for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { + if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { + continue; + } + log.info("start plugin:{}", pluginInfoDO.getPluginId()); + try { +// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId()); +// if (plugin != null) { +// pluginOperator.start(plugin.getPluginId()); +// } + } catch (Exception e) { + log.error("start plugin error", e); + } + } + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java new file mode 100644 index 0000000000..0789c46381 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.iot.service.plugininstance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import jakarta.validation.Valid; + +/** + * IoT 插件实例 Service 接口 + * + * @author 芋道源码 + */ +public interface PluginInstanceService { + + /** + * 创建IoT 插件实例 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO); + + /** + * 更新IoT 插件实例 + * + * @param updateReqVO 更新信息 + */ + void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO); + + /** + * 删除IoT 插件实例 + * + * @param id 编号 + */ + void deletePluginInstance(Long id); + + /** + * 获得IoT 插件实例 + * + * @param id 编号 + * @return IoT 插件实例 + */ + PluginInstanceDO getPluginInstance(Long id); + + /** + * 获得IoT 插件实例分页 + * + * @param pageReqVO 分页查询 + * @return IoT 插件实例分页 + */ + PageResult getPluginInstancePage(PluginInstancePageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java new file mode 100644 index 0000000000..405efe1636 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.iot.service.plugininstance; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS; + +/** + * IoT 插件实例 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class PluginInstanceServiceImpl implements PluginInstanceService { + + @Resource + private PluginInstanceMapper pluginInstanceMapper; + + @Override + public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) { + // 插入 + PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class); + pluginInstanceMapper.insert(pluginInstance); + // 返回 + return pluginInstance.getId(); + } + + @Override + public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) { + // 校验存在 + validatePluginInstanceExists(updateReqVO.getId()); + // 更新 + PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class); + pluginInstanceMapper.updateById(updateObj); + } + + @Override + public void deletePluginInstance(Long id) { + // 校验存在 + validatePluginInstanceExists(id); + // 删除 + pluginInstanceMapper.deleteById(id); + } + + private void validatePluginInstanceExists(Long id) { + if (pluginInstanceMapper.selectById(id) == null) { + throw exception(PLUGIN_INSTANCE_NOT_EXISTS); + } + } + + @Override + public PluginInstanceDO getPluginInstance(Long id) { + return pluginInstanceMapper.selectById(id); + } + + @Override + public PageResult getPluginInstancePage(PluginInstancePageReqVO pageReqVO) { + return pluginInstanceMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml new file mode 100644 index 0000000000..f24f7e14ce --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml new file mode 100644 index 0000000000..2d297c785b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java new file mode 100644 index 0000000000..d921161f8e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java @@ -0,0 +1,171 @@ +package cn.iocoder.yudao.module.iot.service.plugininfo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import jakarta.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link PluginInfoServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(PluginInfoServiceImpl.class) +public class PluginInfoServiceImplTest extends BaseDbUnitTest { + + @Resource + private PluginInfoServiceImpl pluginInfoService; + + @Resource + private PluginInfoMapper pluginInfoMapper; + + @Test + public void testCreatePluginInfo_success() { + // 准备参数 + PluginInfoSaveReqVO createReqVO = randomPojo(PluginInfoSaveReqVO.class).setId(null); + + // 调用 + Long pluginInfoId = pluginInfoService.createPluginInfo(createReqVO); + // 断言 + assertNotNull(pluginInfoId); + // 校验记录的属性是否正确 + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(pluginInfoId); + assertPojoEquals(createReqVO, pluginInfo, "id"); + } + + @Test + public void testUpdatePluginInfo_success() { + // mock 数据 + PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class); + pluginInfoMapper.insert(dbPluginInfo);// @Sql: 先插入出一条存在的数据 + // 准备参数 + PluginInfoSaveReqVO updateReqVO = randomPojo(PluginInfoSaveReqVO.class, o -> { + o.setId(dbPluginInfo.getId()); // 设置更新的 ID + }); + + // 调用 + pluginInfoService.updatePluginInfo(updateReqVO); + // 校验是否更新正确 + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, pluginInfo); + } + + @Test + public void testUpdatePluginInfo_notExists() { + // 准备参数 + PluginInfoSaveReqVO updateReqVO = randomPojo(PluginInfoSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> pluginInfoService.updatePluginInfo(updateReqVO), PLUGIN_INFO_NOT_EXISTS); + } + + @Test + public void testDeletePluginInfo_success() { + // mock 数据 + PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class); + pluginInfoMapper.insert(dbPluginInfo);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbPluginInfo.getId(); + + // 调用 + pluginInfoService.deletePluginInfo(id); + // 校验数据不存在了 + assertNull(pluginInfoMapper.selectById(id)); + } + + @Test + public void testDeletePluginInfo_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> pluginInfoService.deletePluginInfo(id), PLUGIN_INFO_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetPluginInfoPage() { + // mock 数据 + PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class, o -> { // 等会查询到 + o.setPluginId(null); + o.setName(null); + o.setDescription(null); + o.setDeployType(null); + o.setFile(null); + o.setVersion(null); + o.setType(null); + o.setProtocol(null); + o.setStatus(null); + o.setConfigSchema(null); + o.setConfig(null); + o.setScript(null); + o.setCreateTime(null); + }); + pluginInfoMapper.insert(dbPluginInfo); + // 测试 pluginId 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setPluginId(null))); + // 测试 name 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setName(null))); + // 测试 description 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setDescription(null))); + // 测试 deployType 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setDeployType(null))); + // 测试 file 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setFile(null))); + // 测试 version 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setVersion(null))); + // 测试 type 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setType(null))); + // 测试 protocol 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setProtocol(null))); + // 测试 state 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setStatus(null))); + // 测试 configSchema 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setConfigSchema(null))); + // 测试 config 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setConfig(null))); + // 测试 script 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setScript(null))); + // 测试 createTime 不匹配 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setCreateTime(null))); + // 准备参数 + PluginInfoPageReqVO reqVO = new PluginInfoPageReqVO(); + reqVO.setPluginId(null); + reqVO.setName(null); + reqVO.setDescription(null); + reqVO.setDeployType(null); + reqVO.setFile(null); + reqVO.setVersion(null); + reqVO.setType(null); + reqVO.setProtocol(null); + reqVO.setStatus(null); + reqVO.setConfigSchema(null); + reqVO.setConfig(null); + reqVO.setScript(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = pluginInfoService.getPluginInfoPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbPluginInfo, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java new file mode 100644 index 0000000000..fe6235bded --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java @@ -0,0 +1,150 @@ +package cn.iocoder.yudao.module.iot.service.plugininstance; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import jakarta.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link PluginInstanceServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(PluginInstanceServiceImpl.class) +public class PluginInstanceServiceImplTest extends BaseDbUnitTest { + + @Resource + private PluginInstanceServiceImpl pluginInstanceService; + + @Resource + private PluginInstanceMapper pluginInstanceMapper; + + @Test + public void testCreatePluginInstance_success() { + // 准备参数 + PluginInstanceSaveReqVO createReqVO = randomPojo(PluginInstanceSaveReqVO.class).setId(null); + + // 调用 + Long pluginInstanceId = pluginInstanceService.createPluginInstance(createReqVO); + // 断言 + assertNotNull(pluginInstanceId); + // 校验记录的属性是否正确 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectById(pluginInstanceId); + assertPojoEquals(createReqVO, pluginInstance, "id"); + } + + @Test + public void testUpdatePluginInstance_success() { + // mock 数据 + PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class); + pluginInstanceMapper.insert(dbPluginInstance);// @Sql: 先插入出一条存在的数据 + // 准备参数 + PluginInstanceSaveReqVO updateReqVO = randomPojo(PluginInstanceSaveReqVO.class, o -> { + o.setId(dbPluginInstance.getId()); // 设置更新的 ID + }); + + // 调用 + pluginInstanceService.updatePluginInstance(updateReqVO); + // 校验是否更新正确 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, pluginInstance); + } + + @Test + public void testUpdatePluginInstance_notExists() { + // 准备参数 + PluginInstanceSaveReqVO updateReqVO = randomPojo(PluginInstanceSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> pluginInstanceService.updatePluginInstance(updateReqVO), PLUGIN_INSTANCE_NOT_EXISTS); + } + + @Test + public void testDeletePluginInstance_success() { + // mock 数据 + PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class); + pluginInstanceMapper.insert(dbPluginInstance);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbPluginInstance.getId(); + + // 调用 + pluginInstanceService.deletePluginInstance(id); + // 校验数据不存在了 + assertNull(pluginInstanceMapper.selectById(id)); + } + + @Test + public void testDeletePluginInstance_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> pluginInstanceService.deletePluginInstance(id), PLUGIN_INSTANCE_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetPluginInstancePage() { + // mock 数据 + PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class, o -> { // 等会查询到 + o.setMainId(null); + o.setPluginId(null); + o.setIp(null); + o.setPort(null); + o.setHeartbeatAt(null); + o.setCreateTime(null); + }); + pluginInstanceMapper.insert(dbPluginInstance); + // 测试 mainId 不匹配 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setMainId(null))); + // 测试 pluginId 不匹配 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setPluginId(null))); + // 测试 ip 不匹配 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setIp(null))); + // 测试 port 不匹配 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setPort(null))); + // 测试 heartbeatAt 不匹配 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setHeartbeatAt(null))); + // 测试 createTime 不匹配 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setCreateTime(null))); + // 准备参数 + PluginInstancePageReqVO reqVO = new PluginInstancePageReqVO(); + reqVO.setMainId(null); + reqVO.setPluginId(null); + reqVO.setIp(null); + reqVO.setPort(null); + reqVO.setHeartbeatAt(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = pluginInstanceService.getPluginInstancePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbPluginInstance, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/pom.xml new file mode 100644 index 0000000000..49c3215810 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/pom.xml @@ -0,0 +1,31 @@ + + + + yudao-module-iot + cn.iocoder.boot + ${revision} + + 4.0.0 + yudao-module-iot-plugin + jar + + ${project.artifactId} + + 物联网 模块 - 插件 + + + + + cn.iocoder.boot + yudao-common + + + + org.pf4j + pf4j-spring + + + + diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java new file mode 100644 index 0000000000..567dcb038b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java @@ -0,0 +1,6 @@ +/** + * 占位 + * + * TODO 芋艿:后续删除 + */ +package cn.iocoder.yudao.module.iot.plugin; From dea8883f8243b5bd2d67c5213c9b9ac76a24d9dc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 Dec 2024 08:38:12 +0800 Subject: [PATCH 027/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=8F=92=E4=BB=B6=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=9B=B8=E5=85=B3=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/plugin/IotPluginDeployTypeEnum.java | 2 +- .../iot/enums/plugin/IotPluginStatusEnum.java | 9 +- .../iot/enums/plugin/IotPluginTypeEnum.java | 13 +- .../plugininfo/PluginInfoController.java | 2 - .../plugininfo/vo/PluginInfoPageReqVO.java | 8 +- .../admin/plugininfo/vo/PluginInfoRespVO.java | 13 +- .../vo/PluginInstancePageReqVO.java | 1 + .../dataobject/plugininfo/PluginInfoDO.java | 9 +- .../plugininstance/PluginInstanceDO.java | 14 +- .../service/device/IotDeviceDataService.java | 1 - .../plugininfo/PluginInfoServiceImplTest.java | 171 ------------------ .../PluginInstanceServiceImplTest.java | 150 --------------- 12 files changed, 39 insertions(+), 354 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java index fd15514c46..11898f6831 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -13,7 +13,7 @@ import java.util.Arrays; @Getter public enum IotPluginDeployTypeEnum implements IntArrayValuable { - UPLOAD(0, "上传jar"), + UPLOAD(0, "上传 jar"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈 ALONE(1, "独立运行"); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray(); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java index 25b1720225..9aba854b0f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java @@ -42,6 +42,11 @@ public enum IotPluginStatusEnum implements IntArrayValuable { return null; } + @Override + public int[] array() { + return ARRAYS; + } + public static boolean isValidState(Integer state) { return fromState(state) != null; } @@ -50,8 +55,4 @@ public enum IotPluginStatusEnum implements IntArrayValuable { return Arrays.stream(values()).anyMatch(e -> e.getStatus().equals(status)); } - @Override - public int[] array() { - return new int[0]; - } } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java index 9e1e5d7f05..27368d2688 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.enums.plugin; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; import lombok.Getter; import java.util.Arrays; @@ -10,6 +11,7 @@ import java.util.Arrays; * * @author haohao */ +@AllArgsConstructor @Getter public enum IotPluginTypeEnum implements IntArrayValuable { @@ -28,11 +30,12 @@ public enum IotPluginTypeEnum implements IntArrayValuable { */ private final String name; - IotPluginTypeEnum(Integer type, String name) { - this.type = type; - this.name = name; + @Override + public int[] array() { + return ARRAYS; } + // TODO @haohao:可以使用 hutool 简化 public static IotPluginTypeEnum fromType(Integer type) { for (IotPluginTypeEnum value : values()) { if (value.getType().equals(type)) { @@ -46,8 +49,4 @@ public enum IotPluginTypeEnum implements IntArrayValuable { return fromType(type) != null; } - @Override - public int[] array() { - return ARRAYS; - } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java index d87a14590b..b7efd088ef 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java @@ -20,7 +20,6 @@ import org.springframework.web.multipart.MultipartFile; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY; @Tag(name = "管理后台 - IoT 插件信息") @@ -86,7 +85,6 @@ public class PluginInfoController { return success(true); } - // 修改插件状态 @PutMapping("/update-status") @Operation(summary = "修改插件状态") @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java index 1ddb8e1ffc..a3b36da9df 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; import lombok.*; import io.swagger.v3.oas.annotations.media.Schema; import cn.iocoder.yudao.framework.common.pojo.PageParam; @@ -8,13 +10,12 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +// TODO @haohao:只查询必要字段哈 @Schema(description = "管理后台 - IoT 插件信息分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class PluginInfoPageReqVO extends PageParam { - @Schema(description = "插件包id", example = "24627") + @Schema(description = "插件包 ID ", example = "24627") private String pluginId; @Schema(description = "插件名称", example = "赵六") @@ -33,6 +34,7 @@ public class PluginInfoPageReqVO extends PageParam { private String version; @Schema(description = "插件类型", example = "2") + @InEnum(IotPluginTypeEnum.class) private Integer type; @Schema(description = "设备插件协议类型") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java index 1e9f2b7dd6..6f081764a4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java @@ -1,22 +1,23 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; +import lombok.Data; import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; @Schema(description = "管理后台 - IoT 插件信息 Response VO") @Data @ExcelIgnoreUnannotated public class PluginInfoRespVO { - @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") - @ExcelProperty("主键ID") + @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + @ExcelProperty("主键 ID") private Long id; - @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") - @ExcelProperty("插件包id") + @Schema(description = "插件包 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") + @ExcelProperty("插件包 ID") private String pluginId; @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java index 2e678fa9dd..9fccdc0aec 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java @@ -8,6 +8,7 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +// TODO @haohao:建议搞个 plugin,然后里面分 info 和 instance。另外,是不是 info => config 会好点,插件配置? @Schema(description = "管理后台 - IoT 插件实例分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java index 07b2cc7ff9..043cdb9c81 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java @@ -22,12 +22,13 @@ import lombok.*; public class PluginInfoDO extends BaseDO { /** - * 主键ID + * 主键 ID */ @TableId private Long id; + // TODO @haohao:这个是不是改成类似 key 之类的字段哈? /** - * 插件包id + * 插件包 ID */ private String pluginId; /** @@ -41,10 +42,12 @@ public class PluginInfoDO extends BaseDO { /** * 部署方式 */ + // TODO @haohao:枚举 private Integer deployType; /** * 插件包文件名 */ + // TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl? private String file; /** * 插件版本 @@ -53,6 +56,7 @@ public class PluginInfoDO extends BaseDO { /** * 插件类型 */ + // TODO @haohao:枚举 private Integer type; /** * 设备插件协议类型 @@ -61,6 +65,7 @@ public class PluginInfoDO extends BaseDO { /** * 状态 */ + // TODO @haohao:枚举 private Integer status; /** * 插件配置项描述信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java index 79dd762c70..17f295a55f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; +// TODO @haohao:一些必要的关联、枚举 /** * IoT 插件实例 DO * @@ -28,7 +28,7 @@ public class PluginInstanceDO extends BaseDO { @TableId private Long id; /** - * 插件主程序id + * 插件主程序 ID */ private String mainId; /** @@ -44,7 +44,7 @@ public class PluginInstanceDO extends BaseDO { */ private Integer port; /** - * 心跳时间,心路时间超过30秒需要剔除 + * 心跳时间,心路时间超过 30 秒需要剔除 */ private Long heartbeatAt; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java index 4f390ca3be..70fd23014e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -15,7 +15,6 @@ import java.util.Map; */ public interface IotDeviceDataService { - /** * 保存设备数据 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java deleted file mode 100644 index d921161f8e..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java +++ /dev/null @@ -1,171 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugininfo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import jakarta.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import org.springframework.context.annotation.Import; - -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link PluginInfoServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(PluginInfoServiceImpl.class) -public class PluginInfoServiceImplTest extends BaseDbUnitTest { - - @Resource - private PluginInfoServiceImpl pluginInfoService; - - @Resource - private PluginInfoMapper pluginInfoMapper; - - @Test - public void testCreatePluginInfo_success() { - // 准备参数 - PluginInfoSaveReqVO createReqVO = randomPojo(PluginInfoSaveReqVO.class).setId(null); - - // 调用 - Long pluginInfoId = pluginInfoService.createPluginInfo(createReqVO); - // 断言 - assertNotNull(pluginInfoId); - // 校验记录的属性是否正确 - PluginInfoDO pluginInfo = pluginInfoMapper.selectById(pluginInfoId); - assertPojoEquals(createReqVO, pluginInfo, "id"); - } - - @Test - public void testUpdatePluginInfo_success() { - // mock 数据 - PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class); - pluginInfoMapper.insert(dbPluginInfo);// @Sql: 先插入出一条存在的数据 - // 准备参数 - PluginInfoSaveReqVO updateReqVO = randomPojo(PluginInfoSaveReqVO.class, o -> { - o.setId(dbPluginInfo.getId()); // 设置更新的 ID - }); - - // 调用 - pluginInfoService.updatePluginInfo(updateReqVO); - // 校验是否更新正确 - PluginInfoDO pluginInfo = pluginInfoMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, pluginInfo); - } - - @Test - public void testUpdatePluginInfo_notExists() { - // 准备参数 - PluginInfoSaveReqVO updateReqVO = randomPojo(PluginInfoSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> pluginInfoService.updatePluginInfo(updateReqVO), PLUGIN_INFO_NOT_EXISTS); - } - - @Test - public void testDeletePluginInfo_success() { - // mock 数据 - PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class); - pluginInfoMapper.insert(dbPluginInfo);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbPluginInfo.getId(); - - // 调用 - pluginInfoService.deletePluginInfo(id); - // 校验数据不存在了 - assertNull(pluginInfoMapper.selectById(id)); - } - - @Test - public void testDeletePluginInfo_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> pluginInfoService.deletePluginInfo(id), PLUGIN_INFO_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetPluginInfoPage() { - // mock 数据 - PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class, o -> { // 等会查询到 - o.setPluginId(null); - o.setName(null); - o.setDescription(null); - o.setDeployType(null); - o.setFile(null); - o.setVersion(null); - o.setType(null); - o.setProtocol(null); - o.setStatus(null); - o.setConfigSchema(null); - o.setConfig(null); - o.setScript(null); - o.setCreateTime(null); - }); - pluginInfoMapper.insert(dbPluginInfo); - // 测试 pluginId 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setPluginId(null))); - // 测试 name 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setName(null))); - // 测试 description 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setDescription(null))); - // 测试 deployType 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setDeployType(null))); - // 测试 file 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setFile(null))); - // 测试 version 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setVersion(null))); - // 测试 type 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setType(null))); - // 测试 protocol 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setProtocol(null))); - // 测试 state 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setStatus(null))); - // 测试 configSchema 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setConfigSchema(null))); - // 测试 config 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setConfig(null))); - // 测试 script 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setScript(null))); - // 测试 createTime 不匹配 - pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setCreateTime(null))); - // 准备参数 - PluginInfoPageReqVO reqVO = new PluginInfoPageReqVO(); - reqVO.setPluginId(null); - reqVO.setName(null); - reqVO.setDescription(null); - reqVO.setDeployType(null); - reqVO.setFile(null); - reqVO.setVersion(null); - reqVO.setType(null); - reqVO.setProtocol(null); - reqVO.setStatus(null); - reqVO.setConfigSchema(null); - reqVO.setConfig(null); - reqVO.setScript(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = pluginInfoService.getPluginInfoPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbPluginInfo, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java deleted file mode 100644 index fe6235bded..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java +++ /dev/null @@ -1,150 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugininstance; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import jakarta.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import jakarta.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link PluginInstanceServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(PluginInstanceServiceImpl.class) -public class PluginInstanceServiceImplTest extends BaseDbUnitTest { - - @Resource - private PluginInstanceServiceImpl pluginInstanceService; - - @Resource - private PluginInstanceMapper pluginInstanceMapper; - - @Test - public void testCreatePluginInstance_success() { - // 准备参数 - PluginInstanceSaveReqVO createReqVO = randomPojo(PluginInstanceSaveReqVO.class).setId(null); - - // 调用 - Long pluginInstanceId = pluginInstanceService.createPluginInstance(createReqVO); - // 断言 - assertNotNull(pluginInstanceId); - // 校验记录的属性是否正确 - PluginInstanceDO pluginInstance = pluginInstanceMapper.selectById(pluginInstanceId); - assertPojoEquals(createReqVO, pluginInstance, "id"); - } - - @Test - public void testUpdatePluginInstance_success() { - // mock 数据 - PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class); - pluginInstanceMapper.insert(dbPluginInstance);// @Sql: 先插入出一条存在的数据 - // 准备参数 - PluginInstanceSaveReqVO updateReqVO = randomPojo(PluginInstanceSaveReqVO.class, o -> { - o.setId(dbPluginInstance.getId()); // 设置更新的 ID - }); - - // 调用 - pluginInstanceService.updatePluginInstance(updateReqVO); - // 校验是否更新正确 - PluginInstanceDO pluginInstance = pluginInstanceMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, pluginInstance); - } - - @Test - public void testUpdatePluginInstance_notExists() { - // 准备参数 - PluginInstanceSaveReqVO updateReqVO = randomPojo(PluginInstanceSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> pluginInstanceService.updatePluginInstance(updateReqVO), PLUGIN_INSTANCE_NOT_EXISTS); - } - - @Test - public void testDeletePluginInstance_success() { - // mock 数据 - PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class); - pluginInstanceMapper.insert(dbPluginInstance);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbPluginInstance.getId(); - - // 调用 - pluginInstanceService.deletePluginInstance(id); - // 校验数据不存在了 - assertNull(pluginInstanceMapper.selectById(id)); - } - - @Test - public void testDeletePluginInstance_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> pluginInstanceService.deletePluginInstance(id), PLUGIN_INSTANCE_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetPluginInstancePage() { - // mock 数据 - PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class, o -> { // 等会查询到 - o.setMainId(null); - o.setPluginId(null); - o.setIp(null); - o.setPort(null); - o.setHeartbeatAt(null); - o.setCreateTime(null); - }); - pluginInstanceMapper.insert(dbPluginInstance); - // 测试 mainId 不匹配 - pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setMainId(null))); - // 测试 pluginId 不匹配 - pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setPluginId(null))); - // 测试 ip 不匹配 - pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setIp(null))); - // 测试 port 不匹配 - pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setPort(null))); - // 测试 heartbeatAt 不匹配 - pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setHeartbeatAt(null))); - // 测试 createTime 不匹配 - pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setCreateTime(null))); - // 准备参数 - PluginInstancePageReqVO reqVO = new PluginInstancePageReqVO(); - reqVO.setMainId(null); - reqVO.setPluginId(null); - reqVO.setIp(null); - reqVO.setPort(null); - reqVO.setHeartbeatAt(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = pluginInstanceService.getPluginInstancePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbPluginInstance, pageResult.getList().get(0)); - } - -} \ No newline at end of file From 92c2717d461cc117ce0d2c595d3705606a58c287 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 15 Dec 2024 10:46:33 +0800 Subject: [PATCH 028/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=89=B9=E9=87=8F=E5=AF=BC?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 1 + .../admin/device/IotDeviceController.java | 26 ++++++ .../vo/device/IotDeviceImportExcelVO.java | 37 ++++++++ .../vo/device/IotDeviceImportRespVO.java | 23 +++++ .../mysql/device/IotDeviceGroupMapper.java | 4 + .../iot/dal/mysql/device/IotDeviceMapper.java | 4 + .../dal/mysql/product/IotProductMapper.java | 2 +- .../service/device/IotDeviceGroupService.java | 8 ++ .../device/IotDeviceGroupServiceImpl.java | 5 + .../iot/service/device/IotDeviceService.java | 13 ++- .../service/device/IotDeviceServiceImpl.java | 93 +++++++++++++++++-- .../service/product/IotProductService.java | 16 ++++ .../product/IotProductServiceImpl.java | 22 +++-- 13 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 5f58109b36..633b3cb4f2 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -29,6 +29,7 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在"); ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); + ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 35b4abe937..e0c214bc19 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -18,8 +18,10 @@ import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -133,4 +135,28 @@ public class IotDeviceController { new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); } + @PostMapping("/import") + @Operation(summary = "导入设备") + @PreAuthorize("@ss.hasPermission('iot:device:import')") + public CommonResult importDevice( + @RequestParam("file") MultipartFile file, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) + throws Exception { + List list = ExcelUtils.read(file, IotDeviceImportExcelVO.class); + return success(deviceService.importDevice(list, updateSupport)); + } + + @GetMapping("/get-import-template") + @Operation(summary = "获得导入设备模板") + public void importTemplate(HttpServletResponse response) throws IOException { + // 手动创建导出 demo + List list = Arrays.asList( + IotDeviceImportExcelVO.builder().deviceName("温度传感器001").parentDeviceName("gateway110") + .productKey("1de24640dfe").groupNames("灰度分组,生产分组").build(), + IotDeviceImportExcelVO.builder().deviceName("biubiu") + .productKey("YzvHxd4r67sT4s2B").groupNames("").build()); + // 输出 + ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java new file mode 100644 index 0000000000..710e74263d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportExcelVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 设备 Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 设置 chain = false,避免设备导入有问题 +public class IotDeviceImportExcelVO { + + @ExcelProperty("设备名称") + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + @ExcelProperty("父设备名称") + @Schema(description = "父设备名称", example = "网关001") + private String parentDeviceName; + + @ExcelProperty("产品标识") + @NotEmpty(message = "产品标识不能为空") + private String productKey; + + @ExcelProperty("设备分组") + private String groupNames; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java new file mode 100644 index 0000000000..bf52b123f9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceImportRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - IoT 设备导入 Response VO") +@Data +@Builder +public class IotDeviceImportRespVO { + + @Schema(description = "创建成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List createDeviceNames; + + @Schema(description = "更新成功的设备名称数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateDeviceNames; + + @Schema(description = "导入失败的设备集合,key为设备名称,value为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureDeviceNames; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java index 605ce5270a..1f80ae4558 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java @@ -28,4 +28,8 @@ public interface IotDeviceGroupMapper extends BaseMapperX { return selectList(IotDeviceGroupDO::getStatus, status); } + default IotDeviceGroupDO selectByName(String name) { + return selectOne(IotDeviceGroupDO::getName, name); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 59794c4edf..893d7eefda 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -29,6 +29,10 @@ public interface IotDeviceMapper extends BaseMapperX { .orderByDesc(IotDeviceDO::getId)); } + default IotDeviceDO selectByDeviceName(String deviceName) { + return selectOne(IotDeviceDO::getDeviceName, deviceName); + } + default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) { return selectOne(IotDeviceDO::getProductKey, productKey, IotDeviceDO::getDeviceName, deviceName); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index eac1d29e8b..aef149ae72 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -23,7 +23,7 @@ public interface IotProductMapper extends BaseMapperX { } default IotProductDO selectByProductKey(String productKey) { - return selectOne(new LambdaQueryWrapperX().eq(IotProductDO::getProductKey, productKey)); + return selectOne(IotProductDO::getProductKey, productKey); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java index 5219f2d4bd..45e6ab25ef 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java @@ -69,6 +69,14 @@ public interface IotDeviceGroupService { */ IotDeviceGroupDO getDeviceGroup(Long id); + /** + * 获得设备分组 + * + * @param name 名称 + * @return 设备分组 + */ + IotDeviceGroupDO getDeviceGroupByName(String name); + /** * 获得设备分组分页 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java index cfa24784b7..06e5cb11de 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java @@ -76,6 +76,11 @@ public class IotDeviceGroupServiceImpl implements IotDeviceGroupService { return deviceGroupMapper.selectById(id); } + @Override + public IotDeviceGroupDO getDeviceGroupByName(String name) { + return deviceGroupMapper.selectByName(name); + } + @Override public PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) { return deviceGroupMapper.selectPage(pageReqVO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 42101626c0..b8a3511cdd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -6,6 +6,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSa import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportExcelVO; import jakarta.validation.Valid; import javax.annotation.Nullable; @@ -71,7 +73,7 @@ public interface IotDeviceService { IotDeviceDO getDevice(Long id); /** - * 获得设备分页 + * ��得设备分页 * * @param pageReqVO 分页查询 * @return IoT 设备分页 @@ -111,4 +113,13 @@ public interface IotDeviceService { */ IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName); + /** + * 导入设备 + * + * @param importDevices 导入设备列表 + * @param updateSupport 是否支持更新 + * @return 导入结果 + */ + IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 29944eae7b..a43521ff92 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -3,20 +3,22 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; +import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -25,9 +27,7 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.Nullable; import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Objects; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -244,6 +244,15 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); } + /** + * 生成 deviceKey + * + * @return 生成的 deviceKey + */ + private String generateDeviceKey() { + return RandomUtil.randomString(16); + } + /** * 生成 deviceSecret * @@ -282,4 +291,74 @@ public class IotDeviceServiceImpl implements IotDeviceService { return RandomUtil.randomString(32); } + @Override + @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 + public IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport) { + // 1. 参数校验 + if (CollUtil.isEmpty(importDevices)) { + throw exception(DEVICE_IMPORT_LIST_IS_EMPTY); + } + + // 2. 遍历,逐个创建 or 更新 + IotDeviceImportRespVO respVO = IotDeviceImportRespVO.builder().createDeviceNames(new ArrayList<>()) + .updateDeviceNames(new ArrayList<>()).failureDeviceNames(new LinkedHashMap<>()).build(); + importDevices.forEach(importDevice -> { + try { + // 2.1.1 校验字段是否符合要求 + try { + ValidationUtils.validate(importDevice); + } catch (ConstraintViolationException ex){ + respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage()); + return; + } + // 2.1.2 校验产品是否存在 + IotProductDO product = productService.validateProductExists(importDevice.getProductKey()); + // 2.1.3 校验父设备是否存在 + Long gatewayId = null; + if (StrUtil.isNotEmpty(importDevice.getParentDeviceName())) { + IotDeviceDO gatewayDevice = deviceMapper.selectByDeviceName(importDevice.getParentDeviceName()); + if (gatewayDevice == null) { + throw exception(DEVICE_GATEWAY_NOT_EXISTS); + } + if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) { + throw exception(DEVICE_NOT_GATEWAY); + } + gatewayId = gatewayDevice.getId(); + } + // 2.1.4 校验设备分组是否存在 + Set groupIds = new HashSet<>(); + if (StrUtil.isNotEmpty(importDevice.getGroupNames())) { + String[] groupNames = importDevice.getGroupNames().split(","); + for (String groupName : groupNames) { + IotDeviceGroupDO group = deviceGroupService.getDeviceGroupByName(groupName); + if (group == null) { + throw exception(DEVICE_GROUP_NOT_EXISTS); + } + groupIds.add(group.getId()); + } + } + + // 2.2.1 判断如果不存在,在进行插入 + IotDeviceDO existDevice = deviceMapper.selectByDeviceName(importDevice.getDeviceName()); + if (existDevice == null) { + createDevice(new IotDeviceSaveReqVO() + .setDeviceName(importDevice.getDeviceName()).setDeviceKey(generateDeviceKey()) + .setProductId(product.getId()).setGatewayId(gatewayId).setGroupIds(groupIds)); + respVO.getCreateDeviceNames().add(importDevice.getDeviceName()); + return; + } + // 2.2.2 如果存在,判断是否允许更新 + if (updateSupport) { + throw exception(DEVICE_KEY_EXISTS); + } + updateDevice(new IotDeviceSaveReqVO().setId(existDevice.getId()) + .setGatewayId(gatewayId).setGroupIds(groupIds)); + respVO.getUpdateDeviceNames().add(importDevice.getDeviceName()); + } catch (ServiceException ex) { + respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage()); + } + }); + return respVO; + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index bcbf13ef45..9a2e96f671 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -45,6 +45,22 @@ public interface IotProductService { */ IotProductDO getProduct(Long id); + /** + * 校验产品存在 + * + * @param id 编号 + * @return 产品 + */ + IotProductDO validateProductExists(Long id); + + /** + * 校验产品存在 + * + * @param productKey 产品 key + * @return 产品 + */ + IotProductDO validateProductExists(String productKey); + /** * 获得产品分页 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index b1d2d666ba..e7c1443883 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -71,16 +71,26 @@ public class IotProductServiceImpl implements IotProductService { productMapper.deleteById(id); } - private IotProductDO validateProductExists(Long id) { - IotProductDO iotProductDO = productMapper.selectById(id); - if (iotProductDO == null) { + @Override + public IotProductDO validateProductExists(Long id) { + IotProductDO product = productMapper.selectById(id); + if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } - return iotProductDO; + return product; } - private void validateProductStatus(IotProductDO iotProductDO) { - if (Objects.equals(iotProductDO.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { + @Override + public IotProductDO validateProductExists(String productKey) { + IotProductDO product = productMapper.selectByProductKey(productKey); + if (product == null) { + throw exception(PRODUCT_NOT_EXISTS); + } + return product; + } + + private void validateProductStatus(IotProductDO product) { + if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { throw exception(PRODUCT_STATUS_NOT_DELETE); } } From 741096e20861db1ce978e8b3a06178ed04d587cc Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 16 Dec 2024 10:50:48 +0800 Subject: [PATCH 029/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E4=BA=A7=E5=93=81=E7=89=A9=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotProductThingModelAccessModeEnum.java} | 7 +++-- .../IotProductThingModelTypeEnum.java} | 10 +++---- .../thingModel/ThingModelProperty.java | 6 +++-- .../dataType/ThingModelDataType.java | 4 +-- .../vo/IotThinkModelFunctionPageReqVO.java | 4 +-- .../vo/IotThinkModelFunctionSaveReqVO.java | 4 +-- .../convert/device/IotDeviceDataConvert.java | 15 ----------- .../IotThinkModelFunctionConvert.java | 8 +++--- .../dal/dataobject/tdengine/FieldParser.java | 2 +- .../IotThinkModelFunctionDO.java | 4 +-- .../device/IotDeviceDataServiceImpl.java | 6 ++--- .../tdengine/IotSuperTableServiceImpl.java | 7 +++-- .../IotThingModelMessageServiceImpl.java | 6 ++--- .../IotThinkModelFunctionServiceImpl.java | 27 ++++++++++--------- 14 files changed, 48 insertions(+), 62 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/{product/IotAccessModeEnum.java => thingmodel/IotProductThingModelAccessModeEnum.java} (61%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/{product/IotProductFunctionTypeEnum.java => thingmodel/IotProductThingModelTypeEnum.java} (69%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java similarity index 61% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotAccessModeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java index 64ece99ca8..80c96837f1 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.enums.product; +package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,10 +10,9 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotAccessModeEnum { +public enum IotProductThingModelAccessModeEnum { - READ("r"), - WRITE("w"), + READ_ONLY("r"), READ_WRITE("rw"); private final String mode; diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java similarity index 69% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java index c8a03a4d39..153e93ecc8 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductFunctionTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.enums.product; +package cn.iocoder.yudao.module.iot.enums.thingmodel; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; @@ -13,13 +13,13 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum IotProductFunctionTypeEnum implements IntArrayValuable { +public enum IotProductThingModelTypeEnum implements IntArrayValuable { PROPERTY(1, "属性"), SERVICE(2, "服务"), EVENT(3, "事件"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductFunctionTypeEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThingModelTypeEnum::getType).toArray(); /** * 类型 @@ -30,8 +30,8 @@ public enum IotProductFunctionTypeEnum implements IntArrayValuable { */ private final String description; - public static IotProductFunctionTypeEnum valueOfType(Integer type) { - for (IotProductFunctionTypeEnum value : values()) { + public static IotProductThingModelTypeEnum valueOfType(Integer type) { + for (IotProductThingModelTypeEnum value : values()) { if (value.getType().equals(type)) { return value; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java index 025d37e766..46bceaeebc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMod import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType; import lombok.Data; +import java.util.List; + @Data public class ThingModelProperty { @@ -21,7 +23,7 @@ public class ThingModelProperty { private String accessMode; // "rw"、"r"、"w" private Boolean required; - // TODO @haohao:这个是不是 dataSpecs 和 dataSpecsList?https://help.aliyun.com/zh/iot/developer-reference/api-a99t11 - private ThingModelDataType dataType; + private ThingModelDataType dataSpecs; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java index ec5f04bbbc..66284fe24e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; @Data -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "dataType", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = ThingModelIntType.class, name = "int"), @JsonSubTypes.Type(value = ThingModelFloatType.class, name = "float"), @@ -19,6 +19,6 @@ import lombok.Data; }) public abstract class ThingModelDataType { - private String type; + private String dataType; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java index 8a590d4291..48b8f1ab5f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -22,7 +22,7 @@ public class IotThinkModelFunctionPageReqVO extends PageParam { private String name; @Schema(description = "功能类型", example = "1") - @InEnum(IotProductFunctionTypeEnum.class) + @InEnum(IotProductThingModelTypeEnum.class) private Integer type; @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java index 7d51ce5045..a7534d78f6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -38,7 +38,7 @@ public class IotThinkModelFunctionSaveReqVO { @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "功能类型不能为空") - @InEnum(IotProductFunctionTypeEnum.class) + @InEnum(IotProductThingModelTypeEnum.class) private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java index 3bad54ba09..2de0456798 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java @@ -1,23 +1,8 @@ package cn.iocoder.yudao.module.iot.convert.device; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - @Mapper public interface IotDeviceDataConvert { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java index 764d4c030d..f4f4bb87e3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @@ -26,21 +26,21 @@ public interface IotThinkModelFunctionConvert { IotThinkModelFunctionDO convert(IotThinkModelFunctionSaveReqVO bean); default ThingModelProperty convertToProperty(IotThinkModelFunctionSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductFunctionTypeEnum.PROPERTY.getType())) { + if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { return bean.getProperty(); } return null; } default ThingModelEvent convertToEvent(IotThinkModelFunctionSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductFunctionTypeEnum.EVENT.getType())) { + if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { return bean.getEvent(); } return null; } default ThingModelService convertToService(IotThinkModelFunctionSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductFunctionTypeEnum.SERVICE.getType())) { + if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { return bean.getService(); } return null; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 344d65c9a5..daad0b458f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -39,7 +39,7 @@ public class FieldParser { ThingModelDataType type = property.getDataType(); // 将物模型字段类型映射为td字段类型 - String fType = TYPE_MAPPING.get(type.getType().toUpperCase()); + String fType = TYPE_MAPPING.get(type.getDataType().toUpperCase()); // 如果字段类型为NCHAR,默认长度为64 int dataLength = 0; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java index 02b2f97077..d3d8891919 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingMode import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -66,7 +66,7 @@ public class IotThinkModelFunctionDO extends BaseDO { /** * 功能类型 *

- * 枚举 {@link IotProductFunctionTypeEnum} + * 枚举 {@link IotProductThingModelTypeEnum} */ private Integer type; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index d68287cd4b..8fb0edc413 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkMod import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import jakarta.annotation.Resource; @@ -75,7 +75,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 2. 获取设备属性最新数据 List thinkModelFunctionList = thinkModelFunctionService.getThinkModelFunctionListByProductKey(device.getProductKey()); thinkModelFunctionList = thinkModelFunctionList.stream() - .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType() + .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType() .equals(function.getType())).toList(); // 3. 过滤标识符和属性名称 @@ -100,7 +100,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); deviceData.setThinkModelFunctionId(function.getId()); deviceData.setName(function.getName()); - deviceData.setDataType(function.getProperty().getDataType().getType()); + deviceData.setDataType(function.getProperty().getDataType().getDataType()); } list.add(deviceData); }); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java index 20989181ea..1d16651d55 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java @@ -10,8 +10,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -217,8 +216,8 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { thingModel.setProductKey(product.getProductKey()); List properties = functionList.stream() - .filter(function -> IotProductFunctionTypeEnum.PROPERTY.equals( - IotProductFunctionTypeEnum.valueOfType(function.getType()))) + .filter(function -> IotProductThingModelTypeEnum.PROPERTY.equals( + IotProductThingModelTypeEnum.valueOfType(function.getType()))) .map(this::buildThingModelProperty) .collect(Collectors.toList()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 9ffb477150..cf988ad99c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import jakarta.annotation.Resource; @@ -94,7 +94,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ return iotThinkModelFunctionService .getThinkModelFunctionListByProductKey(productKey) .stream() - .filter(function -> IotProductFunctionTypeEnum.PROPERTY.getType().equals(function.getType())) + .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) .toList(); } @@ -138,7 +138,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .deviceId(device.getId()) .thinkModelFunctionId(iotThinkModelFunctionDO.getId()) .name(iotThinkModelFunctionDO.getName()) - .dataType(iotThinkModelFunctionDO.getProperty().getDataType().getType()) + .dataType(iotThinkModelFunctionDO.getProperty().getDataType().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index bfccfb45a1..18a47e808d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -18,8 +18,8 @@ import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunct import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper; -import cn.iocoder.yudao.module.iot.enums.product.IotAccessModeEnum; -import cn.iocoder.yudao.module.iot.enums.product.IotProductFunctionTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; @@ -74,7 +74,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe thinkModelFunctionMapper.insert(function); // 6. 如果创建的是属性,需要更新默认的事件和服务 - if (Objects.equals(createReqVO.getType(), IotProductFunctionTypeEnum.PROPERTY.getType())) { + if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } return function.getId(); @@ -128,7 +128,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe thinkModelFunctionMapper.updateById(thinkModelFunction); // 5. 如果更新的是属性,需要更新默认的事件和服务 - if (Objects.equals(updateReqVO.getType(), IotProductFunctionTypeEnum.PROPERTY.getType())) { + if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } } @@ -156,7 +156,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe thinkModelFunctionMapper.deleteById(id); // 3. 如果删除的是属性,需要更新默认的事件和服务 - if (Objects.equals(functionDO.getType(), IotProductFunctionTypeEnum.PROPERTY.getType())) { + if (Objects.equals(functionDO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(functionDO.getProductId(), functionDO.getProductKey()); } } @@ -210,7 +210,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 List propertyList = thinkModelFunctionMapper - .selectListByProductIdAndType(productId, IotProductFunctionTypeEnum.PROPERTY.getType()); + .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 List newFunctionList = new ArrayList<>(); @@ -237,7 +237,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe List oldFunctionList = thinkModelFunctionMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), - Arrays.asList(IotProductFunctionTypeEnum.EVENT.getType(), IotProductFunctionTypeEnum.SERVICE.getType()) + Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) ); // 3.1 使用 diffList 方法比较新旧列表 @@ -300,7 +300,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe .setIdentifier(event.getIdentifier()) .setName(event.getName()) .setDescription(event.getDescription()) - .setType(IotProductFunctionTypeEnum.EVENT.getType()) + .setType(IotProductThingModelTypeEnum.EVENT.getType()) .setEvent(event); } @@ -314,7 +314,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe .setIdentifier(service.getIdentifier()) .setName(service.getName()) .setDescription(service.getDescription()) - .setType(IotProductFunctionTypeEnum.SERVICE.getType()) + .setType(IotProductThingModelTypeEnum.SERVICE.getType()) .setService(service); } @@ -358,9 +358,10 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } List inputData = new ArrayList<>(); + // TODO @puhui999: 需要重构 for (IotThinkModelFunctionDO functionDO : propertyList) { ThingModelProperty property = functionDO.getProperty(); - if (IotAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { + if (IotProductThingModelAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { ThingModelArgument arg = new ThingModelArgument() .setIdentifier(property.getIdentifier()) .setName(property.getName()) @@ -399,7 +400,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe for (IotThinkModelFunctionDO functionDO : propertyList) { ThingModelProperty property = functionDO.getProperty(); if (ObjectUtils.equalsAny(property.getAccessMode(), - IotAccessModeEnum.READ.getMode(), IotAccessModeEnum.READ_WRITE.getMode())) { + IotProductThingModelAccessModeEnum.READ.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { ThingModelArgument arg = new ThingModelArgument() .setIdentifier(property.getIdentifier()) .setName(property.getName()) @@ -430,10 +431,10 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe // 创建数组类型,元素类型为文本类型(字符串) ThingModelArrayType arrayType = new ThingModelArrayType(); - arrayType.setType("array"); + arrayType.setDataType("array"); ThingModelArraySpecs arraySpecs = new ThingModelArraySpecs(); ThingModelTextType textType = new ThingModelTextType(); - textType.setType("text"); + textType.setDataType("text"); arraySpecs.setItem(textType); arrayType.setSpecs(arraySpecs); inputArg.setDataType(arrayType); From f930f31fababd8b6a0ecdde4d34a253d123ea244 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 16 Dec 2024 12:12:22 +0800 Subject: [PATCH 030/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E4=BA=A7=E5=93=81=E7=89=A9=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E5=B1=9E=E6=80=A7=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotProductThingModelController.java} | 18 +-- .../IotThinkModelFunctionController.http | 0 .../thingmodel}/ThingModelEvent.java | 4 +- .../thingmodel/ThingModelProperty.java | 56 +++++++ .../thingmodel}/ThingModelRespVO.java | 2 +- .../thingmodel}/ThingModelService.java | 4 +- .../dataType/ThingModelArgument.java | 4 +- .../dataType/ThingModelArrayDataSpecs.java | 35 +++++ .../ThingModelBoolOrEnumDataSpecs.java | 28 ++++ .../dataType/ThingModelDataSpecs.java | 34 ++++ .../ThingModelDateOrTextDataSpecs.java | 26 +++ .../dataType/ThingModelNumericDataSpec.java | 47 ++++++ .../vo/IotThinkModelFunctionPageReqVO.java | 2 +- .../vo/IotThinkModelFunctionRespVO.java | 8 +- .../vo/IotThinkModelFunctionSaveReqVO.java | 8 +- .../thingModel/ThingModelProperty.java | 29 ---- .../dataType/ThingModelArraySpecs.java | 17 -- .../dataType/ThingModelArrayType.java | 12 -- .../dataType/ThingModelBoolType.java | 12 -- .../dataType/ThingModelDataType.java | 24 --- .../dataType/ThingModelDateType.java | 10 -- .../dataType/ThingModelDoubleType.java | 18 --- .../dataType/ThingModelEnumType.java | 15 -- .../dataType/ThingModelFloatType.java | 20 --- .../dataType/ThingModelIntType.java | 18 --- .../dataType/ThingModelStructField.java | 13 -- .../dataType/ThingModelStructType.java | 14 -- .../dataType/ThingModelTextType.java | 20 --- .../IotThinkModelFunctionConvert.java | 18 +-- .../dataobject/device/IotDeviceDataDO.java | 10 +- .../IotProductThingModelDO.java} | 12 +- .../dal/dataobject/tdengine/FieldParser.java | 30 ++-- .../IotThinkModelFunctionMapper.java | 62 ++++---- .../device/IotDeviceDataServiceImpl.java | 7 +- .../tdengine/IotSuperTableService.java | 4 +- .../tdengine/IotSuperTableServiceImpl.java | 12 +- .../IotThingModelMessageServiceImpl.java | 27 ++-- .../IotThinkModelFunctionService.java | 14 +- .../IotThinkModelFunctionServiceImpl.java | 148 +++++++++--------- 39 files changed, 424 insertions(+), 418 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction/IotThinkModelFunctionController.java => productthingmodel/IotProductThingModelController.java} (80%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction => productthingmodel}/IotThinkModelFunctionController.http (100%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction/thingModel => productthingmodel/thingmodel}/ThingModelEvent.java (70%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction/thingModel => productthingmodel/thingmodel}/ThingModelRespVO.java (89%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction/thingModel => productthingmodel/thingmodel}/ThingModelService.java (72%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction/thingModel => productthingmodel/thingmodel}/dataType/ThingModelArgument.java (66%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction => productthingmodel}/vo/IotThinkModelFunctionPageReqVO.java (93%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction => productthingmodel}/vo/IotThinkModelFunctionRespVO.java (82%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodelfunction => productthingmodel}/vo/IotThinkModelFunctionSaveReqVO.java (83%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArraySpecs.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArrayType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelBoolType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDateType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDoubleType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelEnumType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelFloatType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelIntType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructField.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructType.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelTextType.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/{thinkmodelfunction/IotThinkModelFunctionDO.java => productthingmodel/IotProductThingModelDO.java} (77%) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java similarity index 80% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java index 4f48f36287..851637c12c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java @@ -1,13 +1,13 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -26,7 +26,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @RestController @RequestMapping("/iot/think-model-function") @Validated -public class IotThinkModelFunctionController { +public class IotProductThingModelController { @Resource private IotThinkModelFunctionService thinkModelFunctionService; @@ -60,7 +60,7 @@ public class IotThinkModelFunctionController { @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('iot:think-model-function:query')") public CommonResult getThinkModelFunction(@RequestParam("id") Long id) { - IotThinkModelFunctionDO function = thinkModelFunctionService.getThinkModelFunction(id); + IotProductThingModelDO function = thinkModelFunctionService.getThinkModelFunction(id); return success(IotThinkModelFunctionConvert.INSTANCE.convert(function)); } @@ -69,7 +69,7 @@ public class IotThinkModelFunctionController { @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('iot:think-model-function:query')") public CommonResult> getThinkModelFunctionListByProductId(@RequestParam("productId") Long productId) { - List list = thinkModelFunctionService.getThinkModelFunctionListByProductId(productId); + List list = thinkModelFunctionService.getThinkModelFunctionListByProductId(productId); return success(IotThinkModelFunctionConvert.INSTANCE.convertList(list)); } @@ -77,7 +77,7 @@ public class IotThinkModelFunctionController { @Operation(summary = "获得产品物模型分页") @PreAuthorize("@ss.hasPermission('iot:think-model-function:query')") public CommonResult> getThinkModelFunctionPage(@Valid IotThinkModelFunctionPageReqVO pageReqVO) { - PageResult pageResult = thinkModelFunctionService.getThinkModelFunctionPage(pageReqVO); + PageResult pageResult = thinkModelFunctionService.getThinkModelFunctionPage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotThinkModelFunctionRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotThinkModelFunctionController.http similarity index 100% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotThinkModelFunctionController.http diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelEvent.java similarity index 70% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEvent.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelEvent.java index d7fa687587..11563dfa86 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelEvent.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; import lombok.Data; import java.util.List; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java new file mode 100644 index 0000000000..b07d00c946 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; + +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import lombok.Data; + +import java.util.List; + +/** + * 物模型中的属性 + * + * dataSpecs 和 dataSpecsList 之中必须传入且只能传入一个 + * + * @author HUIHUI + */ +@Data +public class ThingModelProperty { + + /** + * 属性标识符 + */ + private String identifier; + /** + * 属性名称 + */ + private String name; + /** + * 属性描述 + */ + private String description; + /** + * 云端可以对该属性进行的操作类型 + * 关联枚举 {@link IotProductThingModelAccessModeEnum} + */ + private String accessMode; + /** + * 是否是标准品类的必选服务。 + * + * - true:是 + * - false:否 + */ + private Boolean required; + /** + * 数据类型,与 dataSpecs 的 dataType 保持一致 + */ + private String dataType; + /** + * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 + */ + private ThingModelDataSpecs dataSpecs; + /** + * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 + */ + private List dataSpecsList; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelRespVO.java similarity index 89% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelRespVO.java index d7478e54a4..eeb0937bfb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; import lombok.*; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelService.java similarity index 72% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelService.java index d97e05e9c9..dfcc8a64b6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelService.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; import lombok.Data; import java.util.List; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArgument.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java index 2be24004e8..40d6919718 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; import lombok.Data; @@ -7,7 +7,7 @@ public class ThingModelArgument { private String identifier; private String name; - private ThingModelDataType dataType; + private ThingModelDataSpecs dataType; /** * 用于区分输入或输出参数,"input" 或 "output" */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java new file mode 100644 index 0000000000..4bbf14a5f8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; + +import lombok.Data; + +import java.util.List; + +/** + * 物模型数据类型为数组的 DataSpec 定义 + * + * @author HUIHUI + */ +@Data +public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { + + /** + * 数组中的元素个数。 + */ + private Long size; + /** + * 数组中的元素的数据类型。可选值:struct、int、float、double 或 text。 + */ + private String childDataType; + /** + * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中。 + * 仅当 dataType 不是列表型时,才传入此字段。 + */ + private ThingModelDataSpecs dataSpecs; + /** + * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中。 + * 仅当 dataType 是列表型时,才传入此字段。 + */ + private List dataSpecsList; + +} + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java new file mode 100644 index 0000000000..c763423984 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 物模型数据类型为布尔型或枚举型的 DataSpec 定义 + * + * 数据类型,取值为 bool 或 enum。 + * + * @author HUIHUI + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs { + + /** + * 枚举项的名称。 + * 可包含中文、大小写英文字母、数字、下划线(_)和短划线(-)。 + * 必须以中文、英文字母或数字开头,长度不超过 20 个字符。 + */ + private String name; + /** + * 枚举值。 + */ + private Integer value; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java new file mode 100644 index 0000000000..a4a7847ff8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +/** + * 抽象类 ThingModelDataSpecs + * + * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类。 + * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 + * + * @author HUIHUI + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "dataType", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "int"), + @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "float"), + @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "double"), + @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "text"), + @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "date"), + @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "bool"), + @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "enum"), + @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = "array") +}) +public abstract class ThingModelDataSpecs { + + /** + * 数据类型 + */ + private String dataType; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java new file mode 100644 index 0000000000..f3f2a67ce5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; + +import lombok.Data; + +/** + * 物模型数据类型为时间型或文本型的 DataSpec 定义 + * + * 数据类型,取值为 date 或 text。 + * + * @author HUIHUI + */ +@Data +public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { + + /** + * 数据长度,单位为字节。取值不能超过 2048。 + * 当 dataType 为 text 时,需传入该参数。 + */ + private Long length; + /** + * 默认值,可选参数,用于存储默认值。 + */ + private String defaultValue; + +} + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java new file mode 100644 index 0000000000..9b49631105 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; + +import lombok.Data; + +/** + * 物模型数据类型为数值的 DataSpec 定义 + * + * 数据类型,取值为 int、float 或 double。 + * + * @author HUIHUI + */ +@Data +public class ThingModelNumericDataSpec extends ThingModelDataSpecs { + + /** + * 最大值,需转为字符串类型。值必须与 dataType 类型一致。 + * 例如,当 dataType 为 int 时,取值为 "200",而不是 200。 + */ + private String max; + /** + * 最小值,需转为字符串类型。值必须与 dataType 类型一致。 + * 例如,当 dataType 为 int 时,取值为 "0",而不是 0。 + */ + private String min; + /** + * 步长,需转为字符串类型。值必须与 dataType 类型一致。 + * 例如,当 dataType 为 int 时,取值为 "10",而不是 10。 + */ + private String step; + /** + * 精度。当 dataType 为 float 或 double 时可选传入。 + */ + private String precise; + /** + * 默认值,可传入用于存储的默认值。 + */ + private String defaultValue; + /** + * 单位的符号。 + */ + private String unit; + /** + * 单位的名称。 + */ + private String unitName; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionPageReqVO.java similarity index 93% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionPageReqVO.java index 48b8f1ab5f..548e215735 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionRespVO.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionRespVO.java index e2f3d2ddc6..8ac5acd358 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionRespVO.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionSaveReqVO.java similarity index 83% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionSaveReqVO.java index a7534d78f6..f58018f0bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/vo/IotThinkModelFunctionSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionSaveReqVO.java @@ -1,9 +1,9 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo; +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java deleted file mode 100644 index 46bceaeebc..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/ThingModelProperty.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel; - -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType; -import lombok.Data; - -import java.util.List; - -@Data -public class ThingModelProperty { - - /** - * 属性标识符 - */ - private String identifier; - /** - * 属性名称 - */ - private String name; - /** - * 属性描述 - */ - private String description; - - private String accessMode; // "rw"、"r"、"w" - private Boolean required; - private ThingModelDataType dataSpecs; - private List dataSpecsList; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArraySpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArraySpecs.java deleted file mode 100644 index c3faf61611..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArraySpecs.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -@Data -public class ThingModelArraySpecs { - - /** - * 数组长度 - */ - private int size; - /** - * 数组元素的类型 - */ - private ThingModelDataType item; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArrayType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArrayType.java deleted file mode 100644 index bab87be0ad..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelArrayType.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -// TODO @haohao:这个是不是和别的类,不太统一哈 -@Data -public class ThingModelArrayType extends ThingModelDataType { - - private ThingModelArraySpecs specs; - -} - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelBoolType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelBoolType.java deleted file mode 100644 index b8ca64195d..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelBoolType.java +++ /dev/null @@ -1,12 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) -public class ThingModelBoolType extends ThingModelDataType { - - // Bool 类型一般不需要额外的 specs - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java deleted file mode 100644 index 66284fe24e..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDataType.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import lombok.Data; - -@Data -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "dataType", visible = true) -@JsonSubTypes({ - @JsonSubTypes.Type(value = ThingModelIntType.class, name = "int"), - @JsonSubTypes.Type(value = ThingModelFloatType.class, name = "float"), - @JsonSubTypes.Type(value = ThingModelDoubleType.class, name = "double"), - @JsonSubTypes.Type(value = ThingModelTextType.class, name = "text"), - @JsonSubTypes.Type(value = ThingModelDateType.class, name = "date"), - @JsonSubTypes.Type(value = ThingModelBoolType.class, name = "bool"), - @JsonSubTypes.Type(value = ThingModelEnumType.class, name = "enum"), - @JsonSubTypes.Type(value = ThingModelStructType.class, name = "struct"), - @JsonSubTypes.Type(value = ThingModelArrayType.class, name = "array") -}) -public abstract class ThingModelDataType { - - private String dataType; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDateType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDateType.java deleted file mode 100644 index 8542293398..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDateType.java +++ /dev/null @@ -1,10 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -@Data -public class ThingModelDateType extends ThingModelDataType { - - // Date 类型一般不需要额外的 specs - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDoubleType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDoubleType.java deleted file mode 100644 index e5f3ad268e..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelDoubleType.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -@Data -public class ThingModelDoubleType extends ThingModelDataType { - private ThingModelDoubleSpecs specs; -} - -@Data -class ThingModelDoubleSpecs { - - private Double min; - private Double max; - private Double step; - private String unit; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelEnumType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelEnumType.java deleted file mode 100644 index 3dcb068e97..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelEnumType.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -import java.util.Map; - -@Data -public class ThingModelEnumType extends ThingModelDataType { - - /** - * 枚举值和描述的键值对 - */ - private Map specs; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelFloatType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelFloatType.java deleted file mode 100644 index 27926fa499..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelFloatType.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) -public class ThingModelFloatType extends ThingModelDataType { - private ThingModelFloatSpecs specs; -} - -@Data -class ThingModelFloatSpecs { - - private Float min; - private Float max; - private Float step; - private String unit; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelIntType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelIntType.java deleted file mode 100644 index a126eb7494..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelIntType.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -@Data -public class ThingModelIntType extends ThingModelDataType { - private ThingModelIntSpecs specs; -} - -@Data -class ThingModelIntSpecs { - - private Integer min; - private Integer max; - private Integer step; - private String unit; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructField.java deleted file mode 100644 index 5e079f22b2..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructField.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -@Data -public class ThingModelStructField { - - private String identifier; - private String name; - private ThingModelDataType dataType; - private String description; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructType.java deleted file mode 100644 index f0996513cd..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelStructType.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -import java.util.List; - -@Data -public class ThingModelStructType extends ThingModelDataType { - - private List specs; - -} - - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelTextType.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelTextType.java deleted file mode 100644 index 16d1e402e6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/thingModel/dataType/ThingModelTextType.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType; - -import lombok.Data; - -@Data -public class ThingModelTextType extends ThingModelDataType { - - private ThingModelTextSpecs specs; - -} - -@Data -class ThingModelTextSpecs { - - /** - * 最大长度 - */ - private Integer length; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java index f4f4bb87e3..32ddefc72f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.iot.convert.thinkmodelfunction; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -23,7 +23,7 @@ public interface IotThinkModelFunctionConvert { @Mapping(target = "property", expression = "java(convertToProperty(bean))") @Mapping(target = "event", expression = "java(convertToEvent(bean))") @Mapping(target = "service", expression = "java(convertToService(bean))") - IotThinkModelFunctionDO convert(IotThinkModelFunctionSaveReqVO bean); + IotProductThingModelDO convert(IotThinkModelFunctionSaveReqVO bean); default ThingModelProperty convertToProperty(IotThinkModelFunctionSaveReqVO bean) { if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { @@ -50,8 +50,8 @@ public interface IotThinkModelFunctionConvert { @Mapping(target = "property", source = "property") @Mapping(target = "event", source = "event") @Mapping(target = "service", source = "service") - IotThinkModelFunctionRespVO convert(IotThinkModelFunctionDO bean); + IotThinkModelFunctionRespVO convert(IotProductThingModelDO bean); // 批量转换 - List convertList(List list); + List convertList(List list); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index 892a6b8b76..73e7ee34d8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -30,7 +30,7 @@ public class IotDeviceDataDO { /** * 物模型编号 *

- * 关联 {@link IotThinkModelFunctionDO#getId()} + * 关联 {@link IotProductThingModelDO#getId()} */ private Long thinkModelFunctionId; @@ -51,21 +51,21 @@ public class IotDeviceDataDO { /** * 属性标识符 *

- * 关联 {@link IotThinkModelFunctionDO#getIdentifier()} + * 关联 {@link IotProductThingModelDO#getIdentifier()} */ private String identifier; /** * 属性名称 *

- * 关联 {@link IotThinkModelFunctionDO#getName()} + * 关联 {@link IotProductThingModelDO#getName()} */ private String name; /** * 数据类型 *

- * 关联 {@link IotThinkModelFunctionDO#getProperty()#getDataType()} + * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} */ private String dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java similarity index 77% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java index d3d8891919..71fb1692a6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodelfunction/IotThinkModelFunctionDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java @@ -1,9 +1,9 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction; +package cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -19,7 +19,7 @@ import lombok.NoArgsConstructor; /** * IoT 产品物模型功能 DO *

- * 每个 {@link IotProductDO} 和 {@link IotThinkModelFunctionDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 + * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 * * @author 芋道源码 */ @@ -29,7 +29,7 @@ import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor -public class IotThinkModelFunctionDO extends BaseDO { +public class IotProductThingModelDO extends BaseDO { /** * 物模型功能编号 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index daad0b458f..6dfe54c073 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelDataType; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDataSpecs; import java.util.HashMap; import java.util.List; @@ -36,17 +36,19 @@ public class FieldParser { */ public static TdFieldDO parse(ThingModelProperty property) { String fieldName = property.getIdentifier().toLowerCase(); - ThingModelDataType type = property.getDataType(); - - // 将物模型字段类型映射为td字段类型 - String fType = TYPE_MAPPING.get(type.getDataType().toUpperCase()); - - // 如果字段类型为NCHAR,默认长度为64 - int dataLength = 0; - if ("NCHAR".equals(fType)) { - dataLength = 64; - } - return new TdFieldDO(fieldName, fType, dataLength); + //// TODO @puhui999: 需要重构 + //ThingModelDataSpecs type = property.getDataType(); + // + //// 将物模型字段类型映射为td字段类型 + //String fType = TYPE_MAPPING.get(type.getDataType().toUpperCase()); + // + //// 如果字段类型为NCHAR,默认长度为64 + //int dataLength = 0; + //if ("NCHAR".equals(fType)) { + // dataLength = 64; + //} + //return new TdFieldDO(fieldName, fType, dataLength); + return null; } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java index c8745aab69..8b1496c3ee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -15,48 +15,48 @@ import java.util.List; * @author 芋道源码 */ @Mapper -public interface IotThinkModelFunctionMapper extends BaseMapperX { +public interface IotThinkModelFunctionMapper extends BaseMapperX { - default PageResult selectPage(IotThinkModelFunctionPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotThinkModelFunctionDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotThinkModelFunctionDO::getName, reqVO.getName()) - .eqIfPresent(IotThinkModelFunctionDO::getType, reqVO.getType()) - .eqIfPresent(IotThinkModelFunctionDO::getProductId, reqVO.getProductId()) - .notIn(IotThinkModelFunctionDO::getIdentifier, "get", "set", "post") - .orderByDesc(IotThinkModelFunctionDO::getId)); + default PageResult selectPage(IotThinkModelFunctionPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) + .eqIfPresent(IotProductThingModelDO::getType, reqVO.getType()) + .eqIfPresent(IotProductThingModelDO::getProductId, reqVO.getProductId()) + .notIn(IotProductThingModelDO::getIdentifier, "get", "set", "post") + .orderByDesc(IotProductThingModelDO::getId)); } - default IotThinkModelFunctionDO selectByProductIdAndIdentifier(Long productId, String identifier) { - return selectOne(IotThinkModelFunctionDO::getProductId, productId, - IotThinkModelFunctionDO::getIdentifier, identifier); + default IotProductThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { + return selectOne(IotProductThingModelDO::getProductId, productId, + IotProductThingModelDO::getIdentifier, identifier); } - default List selectListByProductId(Long productId) { - return selectList(IotThinkModelFunctionDO::getProductId, productId); + default List selectListByProductId(Long productId) { + return selectList(IotProductThingModelDO::getProductId, productId); } - default List selectListByProductIdAndType(Long productId, Integer type) { - return selectList(IotThinkModelFunctionDO::getProductId, productId, - IotThinkModelFunctionDO::getType, type); + default List selectListByProductIdAndType(Long productId, Integer type) { + return selectList(IotProductThingModelDO::getProductId, productId, + IotProductThingModelDO::getType, type); } - default List selectListByProductIdAndIdentifiersAndTypes(Long productId, - List identifiers, - List types) { - return selectList(new LambdaQueryWrapperX() - .eq(IotThinkModelFunctionDO::getProductId, productId) - .in(IotThinkModelFunctionDO::getIdentifier, identifiers) - .in(IotThinkModelFunctionDO::getType, types)); + default List selectListByProductIdAndIdentifiersAndTypes(Long productId, + List identifiers, + List types) { + return selectList(new LambdaQueryWrapperX() + .eq(IotProductThingModelDO::getProductId, productId) + .in(IotProductThingModelDO::getIdentifier, identifiers) + .in(IotProductThingModelDO::getType, types)); } - default IotThinkModelFunctionDO selectByProductIdAndName(Long productId, String name) { - return selectOne(IotThinkModelFunctionDO::getProductId, productId, - IotThinkModelFunctionDO::getName, name); + default IotProductThingModelDO selectByProductIdAndName(Long productId, String name) { + return selectOne(IotProductThingModelDO::getProductId, productId, + IotProductThingModelDO::getName, name); } - default List selectListByProductKey(String productKey) { - return selectList(IotThinkModelFunctionDO::getProductKey, productKey); + default List selectListByProductKey(String productKey) { + return selectList(IotProductThingModelDO::getProductKey, productKey); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 8fb0edc413..67a9e84742 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -7,9 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; @@ -73,7 +73,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thinkModelFunctionList = thinkModelFunctionService.getThinkModelFunctionListByProductKey(device.getProductKey()); + List thinkModelFunctionList = thinkModelFunctionService.getThinkModelFunctionListByProductKey(device.getProductKey()); thinkModelFunctionList = thinkModelFunctionList.stream() .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType() .equals(function.getType())).toList(); @@ -90,6 +90,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { .toList(); } // 4. 获取设备属性最新数据 + // TODO @puhui999: 需要重构 thinkModelFunctionList.forEach(function -> { IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), function.getIdentifier()); if (deviceData == null) { @@ -100,7 +101,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); deviceData.setThinkModelFunctionId(function.getId()); deviceData.setName(function.getName()); - deviceData.setDataType(function.getProperty().getDataType().getDataType()); + //deviceData.setDataType(function.getProperty().getDataType().getDataType()); } list.add(deviceData); }); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java index 8aec32f323..e66278ccd3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import java.util.List; @@ -14,5 +14,5 @@ public interface IotSuperTableService { /** * 创建超级表数据模型 */ - void createSuperTableDataModel(IotProductDO product, List functionList); + void createSuperTableDataModel(IotProductDO product, List functionList); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java index 1d16651d55..84e98296ee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import jakarta.annotation.Resource; @@ -33,7 +33,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { private String url; @Override - public void createSuperTableDataModel(IotProductDO product, List functionList) { + public void createSuperTableDataModel(IotProductDO product, List functionList) { ThingModelRespVO thingModel = buildThingModel(product, functionList); if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { @@ -210,7 +210,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 构建物模型 */ - private ThingModelRespVO buildThingModel(IotProductDO product, List functionList) { + private ThingModelRespVO buildThingModel(IotProductDO product, List functionList) { ThingModelRespVO thingModel = new ThingModelRespVO(); thingModel.setId(product.getId()); thingModel.setProductKey(product.getProductKey()); @@ -231,7 +231,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 构建物模型属性 */ - private ThingModelProperty buildThingModelProperty(IotThinkModelFunctionDO function) { + private ThingModelProperty buildThingModelProperty(IotProductThingModelDO function) { ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class); property.setDataType(function.getProperty().getDataType()); return property; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index cf988ad99c..e869980475 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; @@ -71,7 +71,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 Map params = thingModelMessage.dataToMap(); - List functionList = getValidFunctionList(thingModelMessage.getProductKey()); + List functionList = getValidFunctionList(thingModelMessage.getProductKey()); if (functionList.isEmpty()) { return; } @@ -90,7 +90,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .build()); } - private List getValidFunctionList(String productKey) { + private List getValidFunctionList(String productKey) { return iotThinkModelFunctionService .getThinkModelFunctionListByProductKey(productKey) .stream() @@ -98,13 +98,13 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .toList(); } - private List filterAndCollectValidFields(Map params, List functionList, IotDeviceDO device, Long time) { + private List filterAndCollectValidFields(Map params, List functionList, IotDeviceDO device, Long time) { // 1. 获取属性标识符集合 - Set propertyIdentifiers = CollectionUtils.convertSet(functionList, IotThinkModelFunctionDO::getIdentifier); + Set propertyIdentifiers = CollectionUtils.convertSet(functionList, IotProductThingModelDO::getIdentifier); // 2. 构建属性标识符和属性的映射 - Map functionMap = functionList.stream() - .collect(Collectors.toMap(IotThinkModelFunctionDO::getIdentifier, function -> function)); + Map functionMap = functionList.stream() + .collect(Collectors.toMap(IotProductThingModelDO::getIdentifier, function -> function)); // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); @@ -124,21 +124,22 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * 缓存设备属性 * * @param device 设备信息 - * @param iotThinkModelFunctionDO 物模型属性 + * @param iotProductThingModelDO 物模型属性 * @param val 属性值 * @param time 时间 */ - private void setDeviceDataCache(IotDeviceDO device, IotThinkModelFunctionDO iotThinkModelFunctionDO, Object val, Long time) { + private void setDeviceDataCache(IotDeviceDO device, IotProductThingModelDO iotProductThingModelDO, Object val, Long time) { + // TODO @puhui999: 需要重构 IotDeviceDataDO deviceData = IotDeviceDataDO.builder() .productKey(device.getProductKey()) .deviceName(device.getDeviceName()) - .identifier(iotThinkModelFunctionDO.getIdentifier()) + .identifier(iotProductThingModelDO.getIdentifier()) .value(val != null ? val.toString() : null) .updateTime(DateUtil.toLocalDateTime(new Date(time))) .deviceId(device.getId()) - .thinkModelFunctionId(iotThinkModelFunctionDO.getId()) - .name(iotThinkModelFunctionDO.getName()) - .dataType(iotThinkModelFunctionDO.getProperty().getDataType().getDataType()) + .thinkModelFunctionId(iotProductThingModelDO.getId()) + .name(iotProductThingModelDO.getName()) + //.dataType(iotProductThingModelDO.getProperty().getDataType().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java index f60610faff..ce28844531 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.service.thinkmodelfunction; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import jakarta.validation.Valid; import java.util.List; @@ -44,7 +44,7 @@ public interface IotThinkModelFunctionService { * @param id 编号 * @return 产品物模型 */ - IotThinkModelFunctionDO getThinkModelFunction(Long id); + IotProductThingModelDO getThinkModelFunction(Long id); /** * 获得产品物模型列表 @@ -52,7 +52,7 @@ public interface IotThinkModelFunctionService { * @param productId 产品编号 * @return 产品物模型列表 */ - List getThinkModelFunctionListByProductId(Long productId); + List getThinkModelFunctionListByProductId(Long productId); /** * 获得产品物模型分页 @@ -60,7 +60,7 @@ public interface IotThinkModelFunctionService { * @param pageReqVO 分页查询 * @return 产品物模型分页 */ - PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO); + PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO); /** * 创建超级表数据模型 @@ -75,5 +75,5 @@ public interface IotThinkModelFunctionService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getThinkModelFunctionListByProductKey(String productKey); + List getThinkModelFunctionListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java index 18a47e808d..a238789546 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java @@ -4,23 +4,20 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelArgument; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelArraySpecs; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelArrayType; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.thingModel.dataType.ThingModelTextType; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArrayDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDateOrTextDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; @@ -70,7 +67,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe validateProductStatus(createReqVO.getProductId()); // 5. 插入数据库 - IotThinkModelFunctionDO function = IotThinkModelFunctionConvert.INSTANCE.convert(createReqVO); + IotProductThingModelDO function = IotThinkModelFunctionConvert.INSTANCE.convert(createReqVO); thinkModelFunctionMapper.insert(function); // 6. 如果创建的是属性,需要更新默认的事件和服务 @@ -98,14 +95,14 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } private void validateNameUnique(Long productId, String name) { - IotThinkModelFunctionDO function = thinkModelFunctionMapper.selectByProductIdAndName(productId, name); + IotProductThingModelDO function = thinkModelFunctionMapper.selectByProductIdAndName(productId, name); if (function != null) { throw exception(THINK_MODEL_FUNCTION_NAME_EXISTS); } } private void validateIdentifierUnique(Long productId, String identifier) { - IotThinkModelFunctionDO function = thinkModelFunctionMapper.selectByProductIdAndIdentifier(productId, identifier); + IotProductThingModelDO function = thinkModelFunctionMapper.selectByProductIdAndIdentifier(productId, identifier); if (function != null) { throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); } @@ -124,7 +121,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe validateProductStatus(updateReqVO.getProductId()); // 4. 更新数据库 - IotThinkModelFunctionDO thinkModelFunction = IotThinkModelFunctionConvert.INSTANCE.convert(updateReqVO); + IotProductThingModelDO thinkModelFunction = IotThinkModelFunctionConvert.INSTANCE.convert(updateReqVO); thinkModelFunctionMapper.updateById(thinkModelFunction); // 5. 如果更新的是属性,需要更新默认的事件和服务 @@ -134,7 +131,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotThinkModelFunctionDO function = thinkModelFunctionMapper.selectByProductIdAndIdentifier(productId, identifier); + IotProductThingModelDO function = thinkModelFunctionMapper.selectByProductIdAndIdentifier(productId, identifier); if (function != null && ObjectUtil.notEqual(function.getId(), id)) { throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); } @@ -144,7 +141,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe @Transactional(rollbackFor = Exception.class) public void deleteThinkModelFunction(Long id) { // 1. 校验功能是否存在 - IotThinkModelFunctionDO functionDO = thinkModelFunctionMapper.selectById(id); + IotProductThingModelDO functionDO = thinkModelFunctionMapper.selectById(id); if (functionDO == null) { throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } @@ -173,17 +170,17 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } @Override - public IotThinkModelFunctionDO getThinkModelFunction(Long id) { + public IotProductThingModelDO getThinkModelFunction(Long id) { return thinkModelFunctionMapper.selectById(id); } @Override - public List getThinkModelFunctionListByProductId(Long productId) { + public List getThinkModelFunctionListByProductId(Long productId) { return thinkModelFunctionMapper.selectListByProductId(productId); } @Override - public PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO) { + public PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO) { return thinkModelFunctionMapper.selectPage(pageReqVO); } @@ -193,14 +190,14 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe IotProductDO product = productService.getProduct(productId); // 2. 查询产品的物模型功能列表 - List functionList = thinkModelFunctionMapper.selectListByProductId(productId); + List functionList = thinkModelFunctionMapper.selectListByProductId(productId); // 3. 生成 TDengine 的数据模型 dbStructureDataService.createSuperTableDataModel(product, functionList); } @Override - public List getThinkModelFunctionListByProductKey(String productKey) { + public List getThinkModelFunctionListByProductKey(String productKey) { return thinkModelFunctionMapper.selectListByProductKey(productKey); } @@ -209,45 +206,45 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 - List propertyList = thinkModelFunctionMapper + List propertyList = thinkModelFunctionMapper .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 - List newFunctionList = new ArrayList<>(); + List newFunctionList = new ArrayList<>(); // 生成属性上报事件 ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); if (propertyPostEvent != null) { - IotThinkModelFunctionDO eventFunction = buildEventFunctionDO(productId, productKey, propertyPostEvent); + IotProductThingModelDO eventFunction = buildEventFunctionDO(productId, productKey, propertyPostEvent); newFunctionList.add(eventFunction); } // 生成属性设置服务 ThingModelService propertySetService = generatePropertySetService(propertyList); if (propertySetService != null) { - IotThinkModelFunctionDO setServiceFunction = buildServiceFunctionDO(productId, productKey, propertySetService); + IotProductThingModelDO setServiceFunction = buildServiceFunctionDO(productId, productKey, propertySetService); newFunctionList.add(setServiceFunction); } // 生成属性获取服务 ThingModelService propertyGetService = generatePropertyGetService(propertyList); if (propertyGetService != null) { - IotThinkModelFunctionDO getServiceFunction = buildServiceFunctionDO(productId, productKey, propertyGetService); + IotProductThingModelDO getServiceFunction = buildServiceFunctionDO(productId, productKey, propertyGetService); newFunctionList.add(getServiceFunction); } // 3. 获取数据库中的默认的旧事件和服务列表 - List oldFunctionList = thinkModelFunctionMapper.selectListByProductIdAndIdentifiersAndTypes( + List oldFunctionList = thinkModelFunctionMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) ); // 3.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldFunctionList, newFunctionList, + List> diffResult = diffList(oldFunctionList, newFunctionList, // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier()) && Objects.equals(oldFunc.getType(), newFunc.getType())); - List createList = diffResult.get(0); // 需要新增的 - List updateList = diffResult.get(1); // 需要更新的 - List deleteList = diffResult.get(2); // 需要删除的 + List createList = diffResult.get(0); // 需要新增的 + List updateList = diffResult.get(1); // 需要更新的 + List deleteList = diffResult.get(2); // 需要删除的 // 3.2 批量执行数据库操作 // 新增数据库中的新事件和服务列表 @@ -258,14 +255,14 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe if (CollUtil.isNotEmpty(updateList)) { // 首先,为每个需要更新的对象设置其对应的 ID updateList.forEach(updateFunc -> { - IotThinkModelFunctionDO oldFunc = findFunctionByIdentifierAndType( + IotProductThingModelDO oldFunc = findFunctionByIdentifierAndType( oldFunctionList, updateFunc.getIdentifier(), updateFunc.getType()); if (oldFunc != null) { updateFunc.setId(oldFunc.getId()); } }); // 过滤掉没有设置 ID 的对象 - List validUpdateList = updateList.stream() + List validUpdateList = updateList.stream() .filter(func -> func.getId() != null) .collect(Collectors.toList()); // 执行批量更新 @@ -276,7 +273,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe // 删除数据库中的旧事件和服务列表 if (CollUtil.isNotEmpty(deleteList)) { - Set idsToDelete = CollectionUtils.convertSet(deleteList, IotThinkModelFunctionDO::getId); + Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); thinkModelFunctionMapper.deleteByIds(idsToDelete); } } @@ -284,8 +281,8 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe /** * 根据标识符和类型查找功能对象 */ - private IotThinkModelFunctionDO findFunctionByIdentifierAndType(List functionList, - String identifier, Integer type) { + private IotProductThingModelDO findFunctionByIdentifierAndType(List functionList, + String identifier, Integer type) { return CollUtil.findOne(functionList, func -> Objects.equals(func.getIdentifier(), identifier) && Objects.equals(func.getType(), type)); } @@ -293,8 +290,8 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe /** * 构建事件功能对象 */ - private IotThinkModelFunctionDO buildEventFunctionDO(Long productId, String productKey, ThingModelEvent event) { - return new IotThinkModelFunctionDO() + private IotProductThingModelDO buildEventFunctionDO(Long productId, String productKey, ThingModelEvent event) { + return new IotProductThingModelDO() .setProductId(productId) .setProductKey(productKey) .setIdentifier(event.getIdentifier()) @@ -307,8 +304,8 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe /** * 构建服务功能对象 */ - private IotThinkModelFunctionDO buildServiceFunctionDO(Long productId, String productKey, ThingModelService service) { - return new IotThinkModelFunctionDO() + private IotProductThingModelDO buildServiceFunctionDO(Long productId, String productKey, ThingModelService service) { + return new IotProductThingModelDO() .setProductId(productId) .setProductKey(productKey) .setIdentifier(service.getIdentifier()) @@ -321,7 +318,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe /** * 生成属性上报事件 */ - private ThingModelEvent generatePropertyPostEvent(List propertyList) { + private ThingModelEvent generatePropertyPostEvent(List propertyList) { if (CollUtil.isEmpty(propertyList)) { return null; } @@ -335,12 +332,13 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe // 将属性列表转换为事件的输出参数 List outputData = new ArrayList<>(); - for (IotThinkModelFunctionDO functionDO : propertyList) { + // TODO @puhui999: 需要重构 + for (IotProductThingModelDO functionDO : propertyList) { ThingModelProperty property = functionDO.getProperty(); ThingModelArgument arg = new ThingModelArgument() .setIdentifier(property.getIdentifier()) .setName(property.getName()) - .setDataType(property.getDataType()) + //.setDataType(property.getDataType()) .setDescription(property.getDescription()) .setDirection("output"); // 设置为输出参数 outputData.add(arg); @@ -352,24 +350,24 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe /** * 生成属性设置服务 */ - private ThingModelService generatePropertySetService(List propertyList) { + private ThingModelService generatePropertySetService(List propertyList) { if (propertyList == null || propertyList.isEmpty()) { return null; } List inputData = new ArrayList<>(); // TODO @puhui999: 需要重构 - for (IotThinkModelFunctionDO functionDO : propertyList) { + for (IotProductThingModelDO functionDO : propertyList) { ThingModelProperty property = functionDO.getProperty(); - if (IotProductThingModelAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - .setDataType(property.getDataType()) - .setDescription(property.getDescription()) - .setDirection("input"); // 设置为输入参数 - inputData.add(arg); - } + //if (IotProductThingModelAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { + // ThingModelArgument arg = new ThingModelArgument() + // .setIdentifier(property.getIdentifier()) + // .setName(property.getName()) + // .setDataType(property.getDataType()) + // .setDescription(property.getDescription()) + // .setDirection("input"); // 设置为输入参数 + // inputData.add(arg); + //} } if (inputData.isEmpty()) { // 如果没有可写属性,不生成属性设置服务 @@ -391,24 +389,24 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe /** * 生成属性获取服务 */ - private ThingModelService generatePropertyGetService(List propertyList) { + private ThingModelService generatePropertyGetService(List propertyList) { if (propertyList == null || propertyList.isEmpty()) { return null; } - + // TODO @puhui999: 需要重构 List outputData = new ArrayList<>(); - for (IotThinkModelFunctionDO functionDO : propertyList) { + for (IotProductThingModelDO functionDO : propertyList) { ThingModelProperty property = functionDO.getProperty(); - if (ObjectUtils.equalsAny(property.getAccessMode(), - IotProductThingModelAccessModeEnum.READ.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - .setDataType(property.getDataType()) - .setDescription(property.getDescription()) - .setDirection("output"); // 设置为输出参数 - outputData.add(arg); - } + //if (ObjectUtils.equalsAny(property.getAccessMode(), + // IotProductThingModelAccessModeEnum.READ.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { + // ThingModelArgument arg = new ThingModelArgument() + // .setIdentifier(property.getIdentifier()) + // .setName(property.getName()) + // .setDataType(property.getDataType()) + // .setDescription(property.getDescription()) + // .setDirection("output"); // 设置为输出参数 + // outputData.add(arg); + //} } if (outputData.isEmpty()) { // 如果没有可读属性,不生成属性获取服务 @@ -430,13 +428,13 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe .setDirection("input"); // 设置为输入参数 // 创建数组类型,元素类型为文本类型(字符串) - ThingModelArrayType arrayType = new ThingModelArrayType(); + ThingModelArrayDataSpecs arrayType = new ThingModelArrayDataSpecs(); arrayType.setDataType("array"); - ThingModelArraySpecs arraySpecs = new ThingModelArraySpecs(); - ThingModelTextType textType = new ThingModelTextType(); + //ThingModelArraySpecs arraySpecs = new ThingModelArraySpecs(); + ThingModelDateOrTextDataSpecs textType = new ThingModelDateOrTextDataSpecs(); textType.setDataType("text"); - arraySpecs.setItem(textType); - arrayType.setSpecs(arraySpecs); + //arraySpecs.setItem(textType); + //arrayType.setSpecs(arraySpecs); inputArg.setDataType(arrayType); service.setInputData(Collections.singletonList(inputArg)); From 9e98768022f004d0a5832e6e65ab3d29c993ab8e Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 16 Dec 2024 16:41:28 +0800 Subject: [PATCH 031/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E4=BA=A7=E5=93=81=E7=89=A9=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=20CRUD=20=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotProductThingModelController.http | 187 ++++++++++++++++++ .../IotProductThingModelController.java | 56 +++--- .../IotThinkModelFunctionController.http | 112 ----------- .../dataType/ThingModelArrayDataSpecs.java | 16 +- .../ThingModelBoolOrEnumDataSpecs.java | 2 + .../dataType/ThingModelDataSpecs.java | 3 +- .../ThingModelDateOrTextDataSpecs.java | 4 + .../dataType/ThingModelNumericDataSpec.java | 4 + .../dataType/ThingModelStructDataSpecs.java | 58 ++++++ ...ava => IotProductThingModelPageReqVO.java} | 2 +- ...O.java => IotProductThingModelRespVO.java} | 2 +- ...ava => IotProductThingModelSaveReqVO.java} | 2 +- .../IotProductThingModelConvert.java} | 39 ++-- .../IotProductThingModelDO.java | 4 +- ...r.java => IotProductThingModelMapper.java} | 6 +- .../device/IotDeviceDataServiceImpl.java | 6 +- .../product/IotProductServiceImpl.java | 4 +- .../IotProductThingModelService.java} | 23 +-- .../IotProductThingModelServiceImpl.java} | 75 +++---- .../IotThingModelMessageServiceImpl.java | 8 +- 20 files changed, 382 insertions(+), 231 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotThinkModelFunctionController.http create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/{IotThinkModelFunctionPageReqVO.java => IotProductThingModelPageReqVO.java} (94%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/{IotThinkModelFunctionRespVO.java => IotProductThingModelRespVO.java} (98%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/{IotThinkModelFunctionSaveReqVO.java => IotProductThingModelSaveReqVO.java} (97%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/{thinkmodelfunction/IotThinkModelFunctionConvert.java => productthingmodel/IotProductThingModelConvert.java} (66%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/{IotThinkModelFunctionMapper.java => IotProductThingModelMapper.java} (92%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{thinkmodelfunction/IotThinkModelFunctionService.java => productthingmodel/IotProductThingModelService.java} (64%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{thinkmodelfunction/IotThinkModelFunctionServiceImpl.java => productthingmodel/IotProductThingModelServiceImpl.java} (85%) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http new file mode 100644 index 0000000000..7742705ef1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http @@ -0,0 +1,187 @@ +### 请求 /iot/product-thing-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-thing-model/create +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "productId": 12, + "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", + "identifier": "Temperature", + "name": "温度", + "description": "当前温度值", + "type": 1, + "property": { + "identifier": "Temperature", + "name": "温度", + "accessMode": "r", + "required": true, + "dataType": "int", + "dataSpecs": { + "dataType": "int", + "max": "200", + "min": "0", + "step": "10", + "defaultValue": "30", + "unit": "%", + "unitName": "百分比" + }, + "description": "当前温度值" + } +} + +### 请求 /iot/product-thing-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-thing-model/create +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "productId": 12, + "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", + "identifier": "switch", + "name": "开关", + "description": "温度计开关", + "type": 1, + "property": { + "identifier": "switch", + "name": "开关", + "description": "温度计开关", + "accessMode": "rw", + "required": true, + "dataType": "bool", + "dataSpecsList": [ + { + "dataType": "bool", + "name": "关", + "value": 0 + }, + { + "dataType": "bool", + "name": "开", + "value": 1 + } + ] + } +} + +### 请求 /iot/product-thing-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-thing-model/create +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "productId": 12, + "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", + "identifier": "argb", + "name": "温度计 argb 颜色", + "description": "温度计 argb 颜色", + "type": 1, + "property": { + "identifier": "argb", + "name": "温度计 argb 颜色", + "description": "温度计 argb 颜色", + "accessMode": "rw", + "required": true, + "dataType": "array", + "dataSpecs": { + "dataType": "array", + "size": 10, + "childDataType": "struct", + "dataSpecsList": [ + { + "identifier": "switch", + "name": "开关", + "description": "温度计开关", + "accessMode": "rw", + "required": true, + "dataType": "struct", + "childDataType": "bool", + "dataSpecsList": [ + { + "dataType": "bool", + "name": "关", + "value": 0 + }, + { + "dataType": "bool", + "name": "开", + "value": 1 + } + ] + }, + { + "identifier": "Temperature", + "name": "温度", + "accessMode": "r", + "required": true, + "dataType": "struct", + "childDataType": "int", + "dataSpecs": { + "dataType": "int", + "max": "200", + "min": "0", + "step": "10", + "defaultValue": "30", + "unit": "%", + "unitName": "百分比" + }, + "description": "当前温度值" + } + ] + } + } +} + +### 请求 /iot/product-thing-model/update 接口 => 成功 +PUT {{baseUrl}}/iot/product-thing-model/update +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 33, + "productId": 12, + "productKey": "CJVS54fObwZJ9Qe5CJVS54fObwZJ9Qe5", + "identifier": "switch", + "name": "开关", + "description": "温度计开关", + "type": 1, + "property": { + "identifier": "switch", + "name": "开关", + "description": "温度计开关", + "accessMode": "r", + "required": true, + "dataType": "bool", + "dataSpecsList": [ + { + "dataType": "bool", + "name": "关", + "value": 0 + }, + { + "dataType": "bool", + "name": "开", + "value": 1 + } + ] + } +} + +### 请求 /iot/product-thing-model/delete 接口 => 成功 +DELETE {{baseUrl}}/iot/product-thing-model/delete?id=36 +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +### 请求 /iot/product-thing-model/get 接口 => 成功 +GET {{baseUrl}}/iot/product-thing-model/get?id=40 +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + + +### 请求 /iot/product-thing-model/list-by-product-id 接口 => 成功 +GET {{baseUrl}}/iot/product-thing-model/list-by-product-id?productId=1001 +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java index 851637c12c..11a2ffcc50 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java @@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.productthingmodel.IotProductThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; +import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -24,61 +24,61 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管理后台 - IoT 产品物模型") @RestController -@RequestMapping("/iot/think-model-function") +@RequestMapping("/iot/product-thing-model") @Validated public class IotProductThingModelController { @Resource - private IotThinkModelFunctionService thinkModelFunctionService; + private IotProductThingModelService thinkModelFunctionService; @PostMapping("/create") @Operation(summary = "创建产品物模型") - @PreAuthorize("@ss.hasPermission('iot:think-model-function:create')") - public CommonResult createThinkModelFunction(@Valid @RequestBody IotThinkModelFunctionSaveReqVO createReqVO) { - return success(thinkModelFunctionService.createThinkModelFunction(createReqVO)); + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:create')") + public CommonResult createProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO createReqVO) { + return success(thinkModelFunctionService.createProductThingModel(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新产品物模型") - @PreAuthorize("@ss.hasPermission('iot:think-model-function:update')") - public CommonResult updateThinkModelFunction(@Valid @RequestBody IotThinkModelFunctionSaveReqVO updateReqVO) { - thinkModelFunctionService.updateThinkModelFunction(updateReqVO); + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:update')") + public CommonResult updateProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO updateReqVO) { + thinkModelFunctionService.updateProductThingModel(updateReqVO); return success(true); } @DeleteMapping("/delete") @Operation(summary = "删除产品物模型") @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:think-model-function:delete')") - public CommonResult deleteThinkModelFunction(@RequestParam("id") Long id) { - thinkModelFunctionService.deleteThinkModelFunction(id); + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:delete')") + public CommonResult deleteProductThingModel(@RequestParam("id") Long id) { + thinkModelFunctionService.deleteProductThingModel(id); return success(true); } @GetMapping("/get") @Operation(summary = "获得产品物模型") @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:think-model-function:query')") - public CommonResult getThinkModelFunction(@RequestParam("id") Long id) { - IotProductThingModelDO function = thinkModelFunctionService.getThinkModelFunction(id); - return success(IotThinkModelFunctionConvert.INSTANCE.convert(function)); + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") + public CommonResult getProductThingModel(@RequestParam("id") Long id) { + IotProductThingModelDO function = thinkModelFunctionService.getProductThingModel(id); + return success(IotProductThingModelConvert.INSTANCE.convert(function)); } @GetMapping("/list-by-product-id") @Operation(summary = "获得产品物模型") @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:think-model-function:query')") - public CommonResult> getThinkModelFunctionListByProductId(@RequestParam("productId") Long productId) { - List list = thinkModelFunctionService.getThinkModelFunctionListByProductId(productId); - return success(IotThinkModelFunctionConvert.INSTANCE.convertList(list)); + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") + public CommonResult> getProductThingModelListByProductId(@RequestParam("productId") Long productId) { + List list = thinkModelFunctionService.getProductThingModelListByProductId(productId); + return success(IotProductThingModelConvert.INSTANCE.convertList(list)); } @GetMapping("/page") @Operation(summary = "获得产品物模型分页") - @PreAuthorize("@ss.hasPermission('iot:think-model-function:query')") - public CommonResult> getThinkModelFunctionPage(@Valid IotThinkModelFunctionPageReqVO pageReqVO) { - PageResult pageResult = thinkModelFunctionService.getThinkModelFunctionPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotThinkModelFunctionRespVO.class)); + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") + public CommonResult> getProductThingModelPage(@Valid IotProductThingModelPageReqVO pageReqVO) { + PageResult pageResult = thinkModelFunctionService.getProductThingModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotProductThingModelRespVO.class)); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotThinkModelFunctionController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotThinkModelFunctionController.http deleted file mode 100644 index 56464dd80b..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotThinkModelFunctionController.http +++ /dev/null @@ -1,112 +0,0 @@ -### 请求 /iot/think-model-function/create 接口 => 成功 -POST {{baseUrl}}/iot/think-model-function/create -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "productId": 1001, - "productKey": "smart-sensor-001", - "identifier": "Temperature", - "name": "温度", - "description": "当前温度值", - "type": 1, - "property": { - "identifier": "Temperature", - "name": "温度", - "accessMode": "r", - "required": true, - "dataType": { - "type": "float", - "specs": { - "min": -10.0, - "max": 100.0, - "step": 0.1, - "unit": "℃" - } - }, - "description": "当前温度值" - } -} - -### 请求 /iot/think-model-function/create 接口 => 成功 -POST {{baseUrl}}/iot/think-model-function/create -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "productId": 1001, - "productKey": "smart-sensor-001", - "identifier": "Humidity", - "name": "湿度", - "description": "当前湿度值", - "type": 1, - "property": { - "identifier": "Humidity", - "name": "湿度", - "accessMode": "r", - "required": true, - "dataType": { - "type": "float", - "specs": { - "min": 0.0, - "max": 100.0, - "step": 0.1, - "unit": "%" - } - }, - "description": "当前湿度值" - } -} - - - - -### 请求 /iot/think-model-function/update 接口 => 成功 -PUT {{baseUrl}}/iot/think-model-function/update -Content-Type: application/json -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -{ - "id": 11, - "productId": 1001, - "productKey": "smart-sensor-001", - "identifier": "Temperature", - "name": "温度", - "description": "当前温度值", - "type": 1, - "property": { - "identifier": "Temperature", - "name": "温度", - "accessMode": "r", - "required": true, - "dataType": { - "type": "float", - "specs": { - "min": -111.0, - "max": 222.0, - "step": 0.1, - "unit": "℃" - } - }, - "description": "当前温度值" - } -} - -### 请求 /iot/think-model-function/delete 接口 => 成功 -DELETE {{baseUrl}}/iot/think-model-function/delete?id=7 -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - -### 请求 /iot/think-model-function/get 接口 => 成功 -GET {{baseUrl}}/iot/think-model-function/get?id=10 -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} - - -### 请求 /iot/think-model-function/list-by-product-id 接口 => 成功 -GET {{baseUrl}}/iot/think-model-function/list-by-product-id?productId=1001 -tenant-id: {{adminTenentId}} -Authorization: Bearer {{token}} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java index 4bbf14a5f8..5eeef76b93 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.List; @@ -10,24 +13,21 @@ import java.util.List; * @author HUIHUI */ @Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { /** * 数组中的元素个数。 */ - private Long size; + private Integer size; /** * 数组中的元素的数据类型。可选值:struct、int、float、double 或 text。 */ private String childDataType; /** - * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中。 - * 仅当 dataType 不是列表型时,才传入此字段。 - */ - private ThingModelDataSpecs dataSpecs; - /** - * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中。 - * 仅当 dataType 是列表型时,才传入此字段。 + * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中。 + * 此时 struct 取值范围为:int、float、double、text、date、enum、bool */ private List dataSpecsList; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java index c763423984..e2ab9bdbb7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; @@ -12,6 +13,7 @@ import lombok.EqualsAndHashCode; */ @Data @EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java index a4a7847ff8..32090f64bf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java @@ -22,7 +22,8 @@ import lombok.Data; @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "date"), @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "bool"), @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "enum"), - @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = "array") + @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = "array"), + @JsonSubTypes.Type(value = ThingModelStructDataSpecs.class, name = "struct") }) public abstract class ThingModelDataSpecs { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java index f3f2a67ce5..4ce7d2dee9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; +import lombok.EqualsAndHashCode; /** * 物模型数据类型为时间型或文本型的 DataSpec 定义 @@ -10,6 +12,8 @@ import lombok.Data; * @author HUIHUI */ @Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java index 9b49631105..391cbf90aa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; +import lombok.EqualsAndHashCode; /** * 物模型数据类型为数值的 DataSpec 定义 @@ -10,6 +12,8 @@ import lombok.Data; * @author HUIHUI */ @Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 public class ThingModelNumericDataSpec extends ThingModelDataSpecs { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java new file mode 100644 index 0000000000..04c38ed286 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; + +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 物模型数据类型为 struct 的 DataSpec 定义 + * + * @author HUIHUI + */ +@Data +@EqualsAndHashCode(callSuper = true) +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 +public class ThingModelStructDataSpecs extends ThingModelDataSpecs { + + /** + * 属性标识符 + */ + private String identifier; + /** + * 属性名称 + */ + private String name; + /** + * 属性描述 + */ + private String description; + /** + * 云端可以对该属性进行的操作类型 + * 关联枚举 {@link IotProductThingModelAccessModeEnum} + */ + private String accessMode; + /** + * 是否是标准品类的必选服务。 + * + * - true:是 + * - false:否 + */ + private Boolean required; + /** + * struct 数据的数据类型 + */ + private String childDataType; + /** + * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 + */ + private ThingModelDataSpecs dataSpecs; + /** + * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 + */ + private List dataSpecsList; + +} + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelPageReqVO.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelPageReqVO.java index 548e215735..31c5ba1d99 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelPageReqVO.java @@ -13,7 +13,7 @@ import lombok.ToString; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IotThinkModelFunctionPageReqVO extends PageParam { +public class IotProductThingModelPageReqVO extends PageParam { @Schema(description = "功能标识") private String identifier; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelRespVO.java similarity index 98% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelRespVO.java index 8ac5acd358..83c51fb592 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelRespVO.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated -public class IotThinkModelFunctionRespVO { +public class IotProductThingModelRespVO { @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") @ExcelProperty("产品ID") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelSaveReqVO.java similarity index 97% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelSaveReqVO.java index f58018f0bb..e230c39186 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotThinkModelFunctionSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelSaveReqVO.java @@ -12,7 +12,7 @@ import lombok.Data; @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data -public class IotThinkModelFunctionSaveReqVO { +public class IotProductThingModelSaveReqVO { @Schema(description = "编号", example = "1") private Long id; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java index 32ddefc72f..1d57982d56 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodelfunction/IotThinkModelFunctionConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java @@ -1,57 +1,62 @@ -package cn.iocoder.yudao.module.iot.convert.thinkmodelfunction; +package cn.iocoder.yudao.module.iot.convert.productthingmodel; import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; +import org.mapstruct.Named; import org.mapstruct.factory.Mappers; import java.util.List; import java.util.Objects; @Mapper -public interface IotThinkModelFunctionConvert { +public interface IotProductThingModelConvert { - IotThinkModelFunctionConvert INSTANCE = Mappers.getMapper(IotThinkModelFunctionConvert.class); + IotProductThingModelConvert INSTANCE = Mappers.getMapper(IotProductThingModelConvert.class); // 将 SaveReqVO 转换为 DO @Mapping(target = "property", expression = "java(convertToProperty(bean))") @Mapping(target = "event", expression = "java(convertToEvent(bean))") @Mapping(target = "service", expression = "java(convertToService(bean))") - IotProductThingModelDO convert(IotThinkModelFunctionSaveReqVO bean); + IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); - default ThingModelProperty convertToProperty(IotThinkModelFunctionSaveReqVO bean) { + // 将 DO 转换为 RespVO + @Mapping(target = "property", source = "property") + @Mapping(target = "event", source = "event") + @Mapping(target = "service", source = "service") + IotProductThingModelRespVO convert(IotProductThingModelDO bean); + + // 批量转换 + List convertList(List list); + + @Named("convertToProperty") + default ThingModelProperty convertToProperty(IotProductThingModelSaveReqVO bean) { if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { return bean.getProperty(); } return null; } - default ThingModelEvent convertToEvent(IotThinkModelFunctionSaveReqVO bean) { + @Named("convertToEvent") + default ThingModelEvent convertToEvent(IotProductThingModelSaveReqVO bean) { if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { return bean.getEvent(); } return null; } - default ThingModelService convertToService(IotThinkModelFunctionSaveReqVO bean) { + @Named("convertToService") + default ThingModelService convertToService(IotProductThingModelSaveReqVO bean) { if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { return bean.getService(); } return null; } - // 将 DO 转换为 RespVO - @Mapping(target = "property", source = "property") - @Mapping(target = "event", source = "event") - @Mapping(target = "service", source = "service") - IotThinkModelFunctionRespVO convert(IotProductThingModelDO bean); - - // 批量转换 - List convertList(List list); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java index 71fb1692a6..1cececc0cc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java @@ -23,8 +23,8 @@ import lombok.NoArgsConstructor; * * @author 芋道源码 */ -@TableName(value = "iot_think_model_function", autoResultMap = true) -@KeySequence("iot_think_model_function_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName(value = "iot_product_thing_model", autoResultMap = true) +@KeySequence("iot_product_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @Builder @NoArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java similarity index 92% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java index 8b1496c3ee..6ec0d48463 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotThinkModelFunctionMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import org.apache.ibatis.annotations.Mapper; @@ -15,9 +15,9 @@ import java.util.List; * @author 芋道源码 */ @Mapper -public interface IotThinkModelFunctionMapper extends BaseMapperX { +public interface IotProductThingModelMapper extends BaseMapperX { - default PageResult selectPage(IotThinkModelFunctionPageReqVO reqVO) { + default PageResult selectPage(IotProductThingModelPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 67a9e84742..6dbcf0ab7c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -15,7 +15,7 @@ import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; +import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; @@ -40,7 +40,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @Resource private IotThingModelMessageService thingModelMessageService; @Resource - private IotThinkModelFunctionService thinkModelFunctionService; + private IotProductThingModelService thinkModelFunctionService; @Resource private TdEngineDMLMapper tdEngineDMLMapper; @@ -73,7 +73,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thinkModelFunctionList = thinkModelFunctionService.getThinkModelFunctionListByProductKey(device.getProductKey()); + List thinkModelFunctionList = thinkModelFunctionService.getProductThingModelListByProductKey(device.getProductKey()); thinkModelFunctionList = thinkModelFunctionList.stream() .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType() .equals(function.getType())).toList(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index e7c1443883..dabc9ad4a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; +import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -34,7 +34,7 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy - private IotThinkModelFunctionService thinkModelFunctionService; + private IotProductThingModelService thinkModelFunctionService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java similarity index 64% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java index ce28844531..3cc5687563 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.service.thinkmodelfunction; +package cn.iocoder.yudao.module.iot.service.productthingmodel; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; import jakarta.validation.Valid; @@ -13,7 +13,7 @@ import java.util.List; * * @author 芋道源码 */ -public interface IotThinkModelFunctionService { +public interface IotProductThingModelService { /** * 创建产品物模型 @@ -21,7 +21,7 @@ public interface IotThinkModelFunctionService { * @param createReqVO 创建信息 * @return 编号 */ - Long createThinkModelFunction(@Valid IotThinkModelFunctionSaveReqVO createReqVO); + Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); /** @@ -29,14 +29,14 @@ public interface IotThinkModelFunctionService { * * @param updateReqVO 更新信息 */ - void updateThinkModelFunction(@Valid IotThinkModelFunctionSaveReqVO updateReqVO); + void updateProductThingModel(@Valid IotProductThingModelSaveReqVO updateReqVO); /** * 删除产品物模型 * * @param id 编号 */ - void deleteThinkModelFunction(Long id); + void deleteProductThingModel(Long id); /** * 获得产品物模型 @@ -44,7 +44,7 @@ public interface IotThinkModelFunctionService { * @param id 编号 * @return 产品物模型 */ - IotProductThingModelDO getThinkModelFunction(Long id); + IotProductThingModelDO getProductThingModel(Long id); /** * 获得产品物模型列表 @@ -52,7 +52,7 @@ public interface IotThinkModelFunctionService { * @param productId 产品编号 * @return 产品物模型列表 */ - List getThinkModelFunctionListByProductId(Long productId); + List getProductThingModelListByProductId(Long productId); /** * 获得产品物模型分页 @@ -60,7 +60,7 @@ public interface IotThinkModelFunctionService { * @param pageReqVO 分页查询 * @return 产品物模型分页 */ - PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO); + PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); /** * 创建超级表数据模型 @@ -75,5 +75,6 @@ public interface IotThinkModelFunctionService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getThinkModelFunctionListByProductKey(String productKey); + List getProductThingModelListByProductKey(String productKey); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelServiceImpl.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelServiceImpl.java index a238789546..f5efd37bcc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelServiceImpl.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service.thinkmodelfunction; +package cn.iocoder.yudao.module.iot.service.productthingmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; @@ -10,12 +10,12 @@ import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArrayDataSpecs; import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDateOrTextDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotThinkModelFunctionSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thinkmodelfunction.IotThinkModelFunctionConvert; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.productthingmodel.IotProductThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotProductThingModelMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; @@ -41,10 +41,10 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j -public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionService { +public class IotProductThingModelServiceImpl implements IotProductThingModelService { @Resource - private IotThinkModelFunctionMapper thinkModelFunctionMapper; + private IotProductThingModelMapper productThingModelMapper; @Resource private IotProductService productService; @@ -53,7 +53,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe @Override @Transactional(rollbackFor = Exception.class) - public Long createThinkModelFunction(IotThinkModelFunctionSaveReqVO createReqVO) { + public Long createProductThingModel(IotProductThingModelSaveReqVO createReqVO) { // 1. 校验功能标识符在同一产品下是否唯一 validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); @@ -67,12 +67,12 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe validateProductStatus(createReqVO.getProductId()); // 5. 插入数据库 - IotProductThingModelDO function = IotThinkModelFunctionConvert.INSTANCE.convert(createReqVO); - thinkModelFunctionMapper.insert(function); + IotProductThingModelDO function = IotProductThingModelConvert.INSTANCE.convert(createReqVO); + productThingModelMapper.insert(function); // 6. 如果创建的是属性,需要更新默认的事件和服务 if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); + //createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } return function.getId(); } @@ -95,14 +95,14 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } private void validateNameUnique(Long productId, String name) { - IotProductThingModelDO function = thinkModelFunctionMapper.selectByProductIdAndName(productId, name); + IotProductThingModelDO function = productThingModelMapper.selectByProductIdAndName(productId, name); if (function != null) { throw exception(THINK_MODEL_FUNCTION_NAME_EXISTS); } } private void validateIdentifierUnique(Long productId, String identifier) { - IotProductThingModelDO function = thinkModelFunctionMapper.selectByProductIdAndIdentifier(productId, identifier); + IotProductThingModelDO function = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (function != null) { throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); } @@ -110,9 +110,9 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe @Override @Transactional(rollbackFor = Exception.class) - public void updateThinkModelFunction(IotThinkModelFunctionSaveReqVO updateReqVO) { + public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { // 1. 校验功能是否存在 - validateThinkModelFunctionExists(updateReqVO.getId()); + validateproductThingModelMapperExists(updateReqVO.getId()); // 2. 校验功能标识符是否唯一 validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); @@ -121,8 +121,8 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe validateProductStatus(updateReqVO.getProductId()); // 4. 更新数据库 - IotProductThingModelDO thinkModelFunction = IotThinkModelFunctionConvert.INSTANCE.convert(updateReqVO); - thinkModelFunctionMapper.updateById(thinkModelFunction); + IotProductThingModelDO productThingModelDO = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); + productThingModelMapper.updateById(productThingModelDO); // 5. 如果更新的是属性,需要更新默认的事件和服务 if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { @@ -131,7 +131,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotProductThingModelDO function = thinkModelFunctionMapper.selectByProductIdAndIdentifier(productId, identifier); + IotProductThingModelDO function = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (function != null && ObjectUtil.notEqual(function.getId(), id)) { throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); } @@ -139,9 +139,9 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe @Override @Transactional(rollbackFor = Exception.class) - public void deleteThinkModelFunction(Long id) { + public void deleteProductThingModel(Long id) { // 1. 校验功能是否存在 - IotProductThingModelDO functionDO = thinkModelFunctionMapper.selectById(id); + IotProductThingModelDO functionDO = productThingModelMapper.selectById(id); if (functionDO == null) { throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } @@ -150,7 +150,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe validateProductStatus(functionDO.getProductId()); // 2. 删除功能 - thinkModelFunctionMapper.deleteById(id); + productThingModelMapper.deleteById(id); // 3. 如果删除的是属性,需要更新默认的事件和服务 if (Objects.equals(functionDO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { @@ -163,25 +163,25 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe * * @param id 功能编号 */ - private void validateThinkModelFunctionExists(Long id) { - if (thinkModelFunctionMapper.selectById(id) == null) { + private void validateproductThingModelMapperExists(Long id) { + if (productThingModelMapper.selectById(id) == null) { throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } } @Override - public IotProductThingModelDO getThinkModelFunction(Long id) { - return thinkModelFunctionMapper.selectById(id); + public IotProductThingModelDO getProductThingModel(Long id) { + return productThingModelMapper.selectById(id); } @Override - public List getThinkModelFunctionListByProductId(Long productId) { - return thinkModelFunctionMapper.selectListByProductId(productId); + public List getProductThingModelListByProductId(Long productId) { + return productThingModelMapper.selectListByProductId(productId); } @Override - public PageResult getThinkModelFunctionPage(IotThinkModelFunctionPageReqVO pageReqVO) { - return thinkModelFunctionMapper.selectPage(pageReqVO); + public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { + return productThingModelMapper.selectPage(pageReqVO); } @Override @@ -190,23 +190,24 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe IotProductDO product = productService.getProduct(productId); // 2. 查询产品的物模型功能列表 - List functionList = thinkModelFunctionMapper.selectListByProductId(productId); + List functionList = productThingModelMapper.selectListByProductId(productId); // 3. 生成 TDengine 的数据模型 dbStructureDataService.createSuperTableDataModel(product, functionList); } @Override - public List getThinkModelFunctionListByProductKey(String productKey) { - return thinkModelFunctionMapper.selectListByProductKey(productKey); + public List getProductThingModelListByProductKey(String productKey) { + return productThingModelMapper.selectListByProductKey(productKey); } + // TODO @puhui999: 需要重构 /** * 创建默认的事件和服务 */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 - List propertyList = thinkModelFunctionMapper + List propertyList = productThingModelMapper .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 @@ -231,7 +232,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe } // 3. 获取数据库中的默认的旧事件和服务列表 - List oldFunctionList = thinkModelFunctionMapper.selectListByProductIdAndIdentifiersAndTypes( + List oldFunctionList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) @@ -249,7 +250,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe // 3.2 批量执行数据库操作 // 新增数据库中的新事件和服务列表 if (CollUtil.isNotEmpty(createList)) { - thinkModelFunctionMapper.insertBatch(createList); + productThingModelMapper.insertBatch(createList); } // 更新数据库中的事件和服务列表 if (CollUtil.isNotEmpty(updateList)) { @@ -267,14 +268,14 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe .collect(Collectors.toList()); // 执行批量更新 if (CollUtil.isNotEmpty(validUpdateList)) { - thinkModelFunctionMapper.updateBatch(validUpdateList); + productThingModelMapper.updateBatch(validUpdateList); } } // 删除数据库中的旧事件和服务列表 if (CollUtil.isNotEmpty(deleteList)) { Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); - thinkModelFunctionMapper.deleteByIds(idsToDelete); + productThingModelMapper.deleteByIds(idsToDelete); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index e869980475..fdb0ca9e42 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -19,7 +19,7 @@ import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.thinkmodelfunction.IotThinkModelFunctionService; +import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -47,7 +47,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private String url; @Resource - private IotThinkModelFunctionService iotThinkModelFunctionService; + private IotProductThingModelService iotProductThingModelService; @Resource private IotDeviceService iotDeviceService; @Resource @@ -91,8 +91,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ } private List getValidFunctionList(String productKey) { - return iotThinkModelFunctionService - .getThinkModelFunctionListByProductKey(productKey) + return iotProductThingModelService + .getProductThingModelListByProductKey(productKey) .stream() .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) .toList(); From 290fcd94d56334e4706935a5b250c1d925a5eaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 16 Dec 2024 18:18:53 +0800 Subject: [PATCH 032/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E6=94=AF=E6=8C=81=EF=BC=8C=E5=8C=85=E5=90=AB=E6=8F=92?= =?UTF-8?q?=E4=BB=B6API=E5=92=8C=E7=A4=BA=E4=BE=8B=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/pom.xml | 1 + yudao-module-iot/yudao-module-iot-biz/pom.xml | 8 + .../admin/plugininfo/Greetings.java | 40 +++++ .../admin/plugininfo/PluginController.java | 134 ++++++++++++++++ .../admin/plugininfo/WhazzupGreeting.java | 34 ++++ .../framework/plugin/SpringConfiguration.java | 8 + .../service/plugininfo/PluginInfoService.java | 6 +- .../plugininfo/PluginInfoServiceImpl.java | 120 ++++++-------- .../yudao-module-iot-plugin-api/pom.xml | 30 ++++ .../yudao/module/iot/api/Greeting.java | 27 ++++ .../yudao/module/iot/api}/package-info.java | 2 +- .../yudao-module-iot-plugin/plugin.properties | 5 + .../yudao-module-iot-plugin/pom.xml | 148 ++++++++++++++++-- .../src/main/assembly/assembly.xml | 37 +++++ .../iot/plugin/IoTHttpPluginController.java | 22 +++ .../yudao/module/iot/plugin/IoTPlugin.java | 33 ++++ .../module/iot/plugin/IotPluginConfig.java | 16 ++ .../module/iot/plugin/WelcomePlugin.java | 58 +++++++ 18 files changed, 637 insertions(+), 92 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin-api/pom.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java rename yudao-module-iot/{yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin => yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api}/package-info.java (53%) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/plugin.properties create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java diff --git a/yudao-module-iot/pom.xml b/yudao-module-iot/pom.xml index d9002abea5..d47d3c7f64 100644 --- a/yudao-module-iot/pom.xml +++ b/yudao-module-iot/pom.xml @@ -11,6 +11,7 @@ yudao-module-iot-api yudao-module-iot-biz yudao-module-iot-plugin + yudao-module-iot-plugin-api 4.0.0 diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index b003e1785a..774d353809 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -25,6 +25,13 @@ ${revision} + + cn.iocoder.boot + yudao-module-iot-plugin-api + 0.0.1 + compile + + cn.iocoder.boot yudao-spring-boot-starter-biz-tenant @@ -75,6 +82,7 @@ org.pf4j pf4j-spring + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java new file mode 100644 index 0000000000..a8e557f15f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; + +import cn.iocoder.yudao.module.iot.api.Greeting; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 打招呼 测试用例 + */ +@Component +public class Greetings { + + @Autowired + private List greetings; + + public void printGreetings() { + System.out.printf("找到扩展点的 %d 个扩展 '%s'%n", greetings.size(), Greeting.class.getName()); + for (Greeting greeting : greetings) { + System.out.println(">>> " + greeting.getGreeting()); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java new file mode 100644 index 0000000000..c4f9ab4653 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java @@ -0,0 +1,134 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; + +import jakarta.annotation.Resource; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.security.PermitAll; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 插件 Controller 测试用例 + */ +@RestController +@RequestMapping("/iot/plugins") +public class PluginController { + + @Resource + private ApplicationContext applicationContext; + @Resource + private SpringPluginManager springPluginManager; + @Resource + private Greetings greetings; + + @Value("${pf4j.pluginsDir}") + private String pluginsDir; + + /** + * 上传插件 JAR 文件并加载插件 + * + * @param file 上传的 JAR 文件 + * @return 上传结果 + */ + @PermitAll + @PostMapping("/upload") + public ResponseEntity uploadPlugin(@RequestParam("file") MultipartFile file) { + if (file.isEmpty()) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件为空"); + } + + // 确保插件目录存在 + Path pluginsPath = Paths.get(pluginsDir); + try { + if (!Files.exists(pluginsPath)) { + Files.createDirectories(pluginsPath); + } + + // 保存上传的 JAR 文件到插件目录 + String filename = file.getOriginalFilename(); + if (filename == null || (!filename.endsWith(".jar") && !filename.endsWith(".zip"))) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件不是 JAR 或 ZIP 文件"); + } + + Path jarPath = pluginsPath.resolve(filename); + + Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); + + // 加载插件 + String pluginId = springPluginManager.loadPlugin(jarPath.toAbsolutePath()); + + // 启动插件 + springPluginManager.startPlugin(pluginId); + + return ResponseEntity.ok("插件上传并加载成功"); + } catch (IOException e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage()); + } + } + + /** + * 卸载指定插件 + * + * @param pluginId 插件 ID + * @return 卸载结果 + */ + @PermitAll + @DeleteMapping("/unload/{pluginId}") + public ResponseEntity unloadPlugin(@PathVariable String pluginId) { + if (springPluginManager.getPlugins().stream().noneMatch(plugin -> plugin.getDescriptor().getPluginId().equals(pluginId))) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("插件未加载: " + pluginId); + } + + springPluginManager.stopPlugin(pluginId); + springPluginManager.unloadPlugin(pluginId); + + // 删除插件 JAR 文件(可选) +// PluginWrapper plugin = pluginManager.getPlugin(pluginId); +// PluginDescriptor descriptor = plugin.getDescriptor(); +// Path jarPath = Paths.get(pluginsDir).resolve(descriptor.getPluginId() + ".jar"); +// Files.deleteIfExists(jarPath); + + return ResponseEntity.ok("插件卸载成功: " + pluginId); + } + + /** + * 列出所有已加载的插件 + * + * @return 插件列表 + */ + @PermitAll + @GetMapping("/list") + public ResponseEntity> listPlugins() { + List plugins = springPluginManager.getPlugins().stream() + .map(plugin -> plugin.getDescriptor().getPluginId()) + .collect(Collectors.toList()); + return ResponseEntity.ok(plugins); + } + + /** + * 打印问候语 + * + * @return 1 + */ + @PermitAll + @GetMapping("/printGreetings") + public ResponseEntity printGreetings() { + greetings.printGreetings(); + return ResponseEntity.ok(1); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java new file mode 100644 index 0000000000..b6ca7f322b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; + +import cn.iocoder.yudao.module.iot.api.Greeting; +import org.pf4j.Extension; +import org.springframework.stereotype.Component; + +/** + * 打招呼 测试用例 + */ +@Extension +@Component +public class WhazzupGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Whazzup"; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java index f43b69c12c..db03332d90 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.iot.framework.plugin; +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.Greetings; import org.pf4j.spring.SpringPluginManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; @Configuration public class SpringConfiguration { @@ -12,4 +14,10 @@ public class SpringConfiguration { return new SpringPluginManager(); } + @Bean + @DependsOn("pluginManager") + public Greetings greetings() { + return new Greetings(); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java index f9ae486772..8671034021 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java @@ -7,8 +7,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; -import java.io.InputStream; - /** * IoT 插件信息 Service 接口 * @@ -57,7 +55,7 @@ public interface PluginInfoService { /** * 上传插件的 JAR 包 * - * @param id 插件id + * @param id 插件id * @param file 文件 */ void uploadJar(Long id, MultipartFile file); @@ -65,7 +63,7 @@ public interface PluginInfoService { /** * 更新插件的状态 * - * @param id 插件id + * @param id 插件id * @param status 状态 */ void updatePluginStatus(Long id, Integer status); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java index 65ad6ad1db..daa76acc63 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java @@ -17,13 +17,18 @@ import org.pf4j.PluginDescriptor; import org.pf4j.PluginState; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPluginManager; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import org.springframework.web.multipart.MultipartFile; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @@ -47,6 +52,9 @@ public class PluginInfoServiceImpl implements PluginInfoService { @Resource private FileApi fileApi; + @Value("${pf4j.pluginsDir}") + private String pluginsDir; + @Override public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { // 插入 @@ -132,7 +140,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { // 4. 上传插件 String pluginIdNew; try { - String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream())); + String path = fileApi.createFile(pluginsDir, IoUtil.readBytes(file.getInputStream())); Path pluginPath = Path.of(path); pluginIdNew = pluginManager.loadPlugin(pluginPath); } catch (Exception e) { @@ -144,10 +152,31 @@ public class PluginInfoServiceImpl implements PluginInfoService { throw exception(PLUGIN_INSTALL_FAILED); } - // 5. 读取配置文件和脚本 String configJson = ""; String script = ""; + try (JarFile jarFile = new JarFile(pluginWrapper.getPluginPath().toFile())) { + // 5.1 获取config文件在jar包中的路径 + String configFile = "classes/config.json"; + JarEntry configEntry = jarFile.getJarEntry(configFile); + + if (configEntry != null) { + // 5.2 读取配置文件 + configJson = IoUtil.readUtf8(jarFile.getInputStream(configEntry)); + log.info("configJson:{}", configJson); + } + + // 5.3 读取script.js脚本 + String scriptFile = "classes/script.js"; + JarEntry scriptEntity = jarFile.getJarEntry(scriptFile); + if (scriptEntity != null) { + // 5.4 读取脚本文件 + script = IoUtil.readUtf8(jarFile.getInputStream(scriptEntity)); + log.info("script:{}", script); + } + } catch (Exception e) { + throw exception(PLUGIN_INSTALL_FAILED); + } pluginInfoDo.setPluginId(pluginIdNew); pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); @@ -159,48 +188,6 @@ public class PluginInfoServiceImpl implements PluginInfoService { pluginInfoDo.setVersion(pluginDescriptor.getVersion()); pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); pluginInfoMapper.updateById(pluginInfoDo); - - - // 5. 读取配置文件和脚本 -// String configJson = ""; - // String script = ""; -// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) { -// // 5.1 获取config文件在jar包中的路径 -// String configFile = "classes/config.json"; -// JarEntry configEntry = jarFile.getJarEntry(configFile); -// -// if (configEntry != null) { -// // 5.2 读取配置文件 -// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset()); -// log.info("configJson:{}", configJson); -// } -// -// // 5.3 读取script.js脚本 -// String scriptFile = "classes/script.js"; -// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile); -// if (scriptEntity != null) { -// // 5.4 读取脚本文件 -// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset()); -// log.info("script:{}", script); -// } -// } catch (Exception e) { -// throw exception(PLUGIN_INSTALL_FAILED); -// } - - -// PluginState pluginState = pluginInfoUpdate.getPluginState(); -// if (pluginState == PluginState.STARTED) { -// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus()); -// } -// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId()); -// pluginInfoDo.setFile(file.getOriginalFilename()); -// pluginInfoDo.setConfigSchema(configJson); -// pluginInfoDo.setScript(script); -// -// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor(); -// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion()); -// pluginInfoDo.setDescription(pluginDescriptor.getDescription()); -// pluginInfoMapper.updateById(pluginInfoDo); } @Override @@ -213,26 +200,22 @@ public class PluginInfoServiceImpl implements PluginInfoService { throw exception(PLUGIN_STATUS_INVALID); } - // 插件包为空 -// String pluginId = pluginInfoDo.getPluginId(); -// if (StrUtil.isBlank(pluginId)) { -// throw exception(PLUGIN_INFO_NOT_EXISTS); -// } -// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId); -// if (pluginInfo != null) { -// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) { -// // 启动插件 -// pluginOperator.start(pluginId); -// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) { -// // 停止插件 -// pluginOperator.stop(pluginId); -// } -// } else { -// // 已经停止,未获取到插件 -// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { -// throw exception(PLUGIN_STATUS_INVALID); -// } -// } + String pluginId = pluginInfoDo.getPluginId(); + PluginWrapper plugin = pluginManager.getPlugin(pluginId); + if (plugin != null) { + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { + // 启动插件 + pluginManager.startPlugin(pluginId); + } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { + // 停止插件 + pluginManager.stopPlugin(pluginId); + } + } else { + // 已经停止,未获取到插件 + if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { + throw exception(PLUGIN_STATUS_INVALID); + } + } pluginInfoDo.setStatus(status); pluginInfoMapper.updateById(pluginInfoDo); } @@ -244,20 +227,13 @@ public class PluginInfoServiceImpl implements PluginInfoService { @SneakyThrows private void startPlugins() { -// while (!pluginOperator.inited()) { -// Thread.sleep(1000L); -// } - for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { continue; } log.info("start plugin:{}", pluginInfoDO.getPluginId()); try { -// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId()); -// if (plugin != null) { -// pluginOperator.start(plugin.getPluginId()); -// } + pluginManager.startPlugin(pluginInfoDO.getPluginId()); } catch (Exception e) { log.error("start plugin error", e); } diff --git a/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml b/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml new file mode 100644 index 0000000000..e04e818deb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + cn.iocoder.boot + yudao-module-iot-plugin-api + 0.0.1 + jar + + ${project.artifactId} + + 物联网 模块插件 API,暴露给其它模块调用 + + + + 0.9.0 + + + + + + org.pf4j + pf4j-spring + ${pf4j-spring.version} + provided + + + + diff --git a/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java b/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java new file mode 100644 index 0000000000..b284549373 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.iocoder.yudao.module.iot.api; + +import org.pf4j.ExtensionPoint; + +/** + * @author Decebal Suiu + */ +public interface Greeting extends ExtensionPoint { + + String getGreeting(); + +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java b/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java similarity index 53% rename from yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java rename to yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java index 567dcb038b..7da0c665ba 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java +++ b/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java @@ -3,4 +3,4 @@ * * TODO 芋艿:后续删除 */ -package cn.iocoder.yudao.module.iot.plugin; +package cn.iocoder.yudao.module.iot.api; diff --git a/yudao-module-iot/yudao-module-iot-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/plugin.properties new file mode 100644 index 0000000000..bbfe3185c6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/plugin.properties @@ -0,0 +1,5 @@ +plugin.id=iot-plugin-hppt +plugin.class=cn.iocoder.yudao.module.iot.plugin.WelcomePlugin +plugin.version=0.0.1 +plugin.provider=ahh +plugin.dependencies= diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/pom.xml index 49c3215810..52e58d57b4 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/pom.xml @@ -1,31 +1,149 @@ - - - yudao-module-iot - cn.iocoder.boot - ${revision} - + 4.0.0 + cn.iocoder.boot yudao-module-iot-plugin + 0.0.1 jar ${project.artifactId} - - 物联网 模块 - 插件 - + 物联网 模块 - 插件 + + + + iot-plugin-http + cn.iocoder.yudao.module.iot.plugin.WelcomePlugin + 0.0.1 + ahh + + + + 17 + ${java.version} + ${java.version} + 1.6 + 2.3 + 2.4 + 0.9.0 + + 3.3.1 + UTF-8 + + - cn.iocoder.boot - yudao-common + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + provided - + org.pf4j pf4j-spring + ${pf4j-spring.version} + provided + + + + cn.iocoder.boot + yudao-module-iot-plugin-api + ${project.version} - + + + + + + org.apache.maven.plugins + maven-antrun-plugin + ${maven-antrun-plugin.version} + + + unzip jar file + package + + + + + + + run + + + + + + + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + + src/main/assembly/assembly.xml + + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.dependencies} + + + + + + + maven-deploy-plugin + + true + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..ce2e92cf95 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml @@ -0,0 +1,37 @@ + + + plugin + + zip + + false + + + false + runtime + lib + + *:jar:* + + + + + + + target/plugin-classes + classes + + + diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java new file mode 100644 index 0000000000..a6633388e5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.plugin; + + +import org.pf4j.Extension; +import org.pf4j.ExtensionPoint; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/iot/plugin-demo") +@Extension +public class IoTHttpPluginController implements ExtensionPoint { + + @GetMapping("/greet") + public String greet() { + return "Hello from MyPlugin!"; + } + + @PostMapping("/message") + public void receiveMessage(@RequestBody String message) { + System.out.println("Received message: " + message); + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java new file mode 100644 index 0000000000..78c07c404f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPlugin; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +public class IoTPlugin extends SpringPlugin { + public IoTPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + System.out.println("IoTPlugin 启动"); + } + + @Override + public void stop() { + System.out.println("IoTPlugin 停止"); + super.stop(); // to close applicationContext + } + + @Override + protected ApplicationContext createApplicationContext() { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); + applicationContext.register(IoTHttpPluginController.class); // 注册 IoTPluginConfig + applicationContext.refresh(); + System.out.println("IoTPlugin 加载完成"); + return applicationContext; + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java new file mode 100644 index 0000000000..02b8bb5a04 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.server.ConfigurableWebServerFactory; +import org.springframework.context.annotation.Bean; + +@Configuration +public class IoTPluginConfig { + + @Bean + public IoTHttpPluginController ioTHttpPluginController() { + return new IoTHttpPluginController(); + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java new file mode 100644 index 0000000000..5c53cf8a86 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.iocoder.yudao.module.iot.api.Greeting; +import org.apache.commons.lang.StringUtils; +import org.pf4j.Extension; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; + +/** + * 打招呼 测试用例 + */ +public class WelcomePlugin extends Plugin { + + public WelcomePlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + System.out.println("WelcomePlugin.start()"); + // for testing the development mode + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + System.out.println(StringUtils.upperCase("WelcomePlugin")); + } + } + + @Override + public void stop() { + System.out.println("WelcomePlugin.stop()"); + } + + @Extension + public static class WelcomeGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Welcome to PF4J"; + } + + } + +} \ No newline at end of file From ce4912304388f6dd4b38cdd1718b90019aa7578b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 16 Dec 2024 18:43:08 +0800 Subject: [PATCH 033/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E3=80=91IoT=EF=BC=9A=E6=9B=B4=E6=96=B0=E9=97=AE?= =?UTF-8?q?=E5=80=99=E8=AF=AD=E6=89=93=E5=8D=B0=E6=96=B9=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E9=97=AE=E5=80=99=E8=AF=AD=E6=95=B0=E9=87=8F?= =?UTF-8?q?=EF=BC=9B=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E6=8F=92=E4=BB=B6=E6=8E=A7=E5=88=B6=E5=99=A8=E5=92=8C?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/plugininfo/Greetings.java | 5 +-- .../admin/plugininfo/PluginController.java | 6 ++-- .../iot/plugin/IoTHttpPluginController.java | 22 ------------- .../yudao/module/iot/plugin/IoTPlugin.java | 33 ------------------- .../module/iot/plugin/IotPluginConfig.java | 16 --------- 5 files changed, 6 insertions(+), 76 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java index a8e557f15f..1b29a34475 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java @@ -30,11 +30,12 @@ public class Greetings { @Autowired private List greetings; - public void printGreetings() { + public Integer printGreetings() { System.out.printf("找到扩展点的 %d 个扩展 '%s'%n", greetings.size(), Greeting.class.getName()); for (Greeting greeting : greetings) { System.out.println(">>> " + greeting.getGreeting()); } + return greetings.size(); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java index c4f9ab4653..4cd22bccaf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java @@ -123,12 +123,12 @@ public class PluginController { /** * 打印问候语 * - * @return 1 + * @return 问候语数量 */ @PermitAll @GetMapping("/printGreetings") public ResponseEntity printGreetings() { - greetings.printGreetings(); - return ResponseEntity.ok(1); + Integer count = greetings.printGreetings(); + return ResponseEntity.ok(count); } } diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java deleted file mode 100644 index a6633388e5..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - - -import org.pf4j.Extension; -import org.pf4j.ExtensionPoint; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/iot/plugin-demo") -@Extension -public class IoTHttpPluginController implements ExtensionPoint { - - @GetMapping("/greet") - public String greet() { - return "Hello from MyPlugin!"; - } - - @PostMapping("/message") - public void receiveMessage(@RequestBody String message) { - System.out.println("Received message: " + message); - } -} diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java deleted file mode 100644 index 78c07c404f..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - -import org.pf4j.PluginWrapper; -import org.pf4j.spring.SpringPlugin; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; - -public class IoTPlugin extends SpringPlugin { - public IoTPlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - System.out.println("IoTPlugin 启动"); - } - - @Override - public void stop() { - System.out.println("IoTPlugin 停止"); - super.stop(); // to close applicationContext - } - - @Override - protected ApplicationContext createApplicationContext() { - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); - applicationContext.register(IoTHttpPluginController.class); // 注册 IoTPluginConfig - applicationContext.refresh(); - System.out.println("IoTPlugin 加载完成"); - return applicationContext; - } -} diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java deleted file mode 100644 index 02b8bb5a04..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.boot.web.server.WebServerFactoryCustomizer; -import org.springframework.boot.web.server.ConfigurableWebServerFactory; -import org.springframework.context.annotation.Bean; - -@Configuration -public class IoTPluginConfig { - - @Bean - public IoTHttpPluginController ioTHttpPluginController() { - return new IoTHttpPluginController(); - } -} From 0245aac530f8b07305f6feeb14ff7dbf035ee8ab Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 16 Dec 2024 20:42:07 +0800 Subject: [PATCH 034/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E8=AF=84=E5=AE=A1=20thinkmodel?= =?UTF-8?q?=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/thingmodel/IotProductThingModelAccessModeEnum.java | 2 +- .../iot/enums/thingmodel/IotProductThingModelTypeEnum.java | 1 + .../thingmodel/dataType/ThingModelArgument.java | 1 + .../thingmodel/dataType/ThingModelArrayDataSpecs.java | 1 - .../yudao/module/iot/convert/device/IotDeviceDataConvert.java | 1 + .../service/productthingmodel/IotProductThingModelService.java | 1 - 6 files changed, 4 insertions(+), 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java index 80c96837f1..a755018034 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java @@ -4,7 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; /** - * IOT 访问方式枚举类 + * IOT 访问方式枚举类 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java index 153e93ecc8..a12299e993 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java @@ -6,6 +6,7 @@ import lombok.Getter; import java.util.Arrays; +// TODO @芋艿:纠结下,到底叫 thinkmodel 好,还是 function 好 /** * IOT 产品功能(物模型)类型枚举类 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java index 40d6919718..289bb60108 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java @@ -1,3 +1,4 @@ +// TODO @puhui999:productthingmodel 是不是不要这层包了 package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; import lombok.Data; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java index 5eeef76b93..3be26e296b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java index 2de0456798..d8f6e47042 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.convert.device; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; +// TODO 是否要删除? @Mapper public interface IotDeviceDataConvert { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java index 3cc5687563..413b6b0143 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java @@ -23,7 +23,6 @@ public interface IotProductThingModelService { */ Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); - /** * 更新产品物模型 * From 91b817a9ecd1b22d2ea1ee5e0aa1a17f4b2bbd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 16 Dec 2024 22:25:01 +0800 Subject: [PATCH 035/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E3=80=91IoT=EF=BC=9A=E5=88=A0=E9=99=A4=20WelcomePlugi?= =?UTF-8?q?n=EF=BC=8C=E6=96=B0=E5=A2=9E=20HttpPlugin=20=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20HTTP=20=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/pom.xml | 2 +- .../yudao-module-iot-plugin-api/pom.xml | 4 +- .../yudao-module-iot-plugin/plugin.properties | 5 - .../yudao-module-iot-plugin/pom.xml | 149 ++--------------- .../module/iot/plugin/WelcomePlugin.java | 58 ------- .../plugin.properties | 5 + .../yudao-module-iot-http-plugin/pom.xml | 156 ++++++++++++++++++ .../src/main/assembly/assembly.xml | 0 .../yudao/module/iot/plugin/HttpPlugin.java | 79 +++++++++ 9 files changed, 253 insertions(+), 205 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/plugin.properties delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml rename yudao-module-iot/yudao-module-iot-plugin/{ => yudao-module-iot-http-plugin}/src/main/assembly/assembly.xml (100%) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java diff --git a/yudao-module-iot/pom.xml b/yudao-module-iot/pom.xml index d47d3c7f64..ecfefab64d 100644 --- a/yudao-module-iot/pom.xml +++ b/yudao-module-iot/pom.xml @@ -10,8 +10,8 @@ yudao-module-iot-api yudao-module-iot-biz - yudao-module-iot-plugin yudao-module-iot-plugin-api + yudao-module-iot-plugin 4.0.0 diff --git a/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml b/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml index e04e818deb..6d5eb765b1 100644 --- a/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 cn.iocoder.boot diff --git a/yudao-module-iot/yudao-module-iot-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/plugin.properties deleted file mode 100644 index bbfe3185c6..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/plugin.properties +++ /dev/null @@ -1,5 +0,0 @@ -plugin.id=iot-plugin-hppt -plugin.class=cn.iocoder.yudao.module.iot.plugin.WelcomePlugin -plugin.version=0.0.1 -plugin.provider=ahh -plugin.dependencies= diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/pom.xml index 52e58d57b4..8ec68638f3 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/pom.xml @@ -1,149 +1,20 @@ + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 cn.iocoder.boot yudao-module-iot-plugin 0.0.1 - jar + pom + + + yudao-module-iot-http-plugin + ${project.artifactId} - 物联网 模块 - 插件 + + 物联网模块 - 插件模块 + - - - iot-plugin-http - cn.iocoder.yudao.module.iot.plugin.WelcomePlugin - 0.0.1 - ahh - - - - 17 - ${java.version} - ${java.version} - 1.6 - 2.3 - 2.4 - 0.9.0 - - 3.3.1 - UTF-8 - - - - - - org.springframework.boot - spring-boot-starter-web - ${spring.boot.version} - provided - - - - org.pf4j - pf4j-spring - ${pf4j-spring.version} - provided - - - - cn.iocoder.boot - yudao-module-iot-plugin-api - ${project.version} - - - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - ${maven-antrun-plugin.version} - - - unzip jar file - package - - - - - - - run - - - - - - - maven-assembly-plugin - ${maven-assembly-plugin.version} - - - - src/main/assembly/assembly.xml - - - false - - - - make-assembly - package - - attached - - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - - ${plugin.id} - ${plugin.class} - ${plugin.version} - ${plugin.provider} - ${plugin.dependencies} - - - - - - - maven-deploy-plugin - - true - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java deleted file mode 100644 index 5c53cf8a86..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package cn.iocoder.yudao.module.iot.plugin; - -import cn.iocoder.yudao.module.iot.api.Greeting; -import org.apache.commons.lang.StringUtils; -import org.pf4j.Extension; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; - -/** - * 打招呼 测试用例 - */ -public class WelcomePlugin extends Plugin { - - public WelcomePlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - System.out.println("WelcomePlugin.start()"); - // for testing the development mode - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { - System.out.println(StringUtils.upperCase("WelcomePlugin")); - } - } - - @Override - public void stop() { - System.out.println("WelcomePlugin.stop()"); - } - - @Extension - public static class WelcomeGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Welcome to PF4J"; - } - - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties new file mode 100644 index 0000000000..694c97ba5f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties @@ -0,0 +1,5 @@ +plugin.id=http-plugin +plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpPlugin +plugin.version=0.0.1 +plugin.provider=ahh +plugin.dependencies= diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml new file mode 100644 index 0000000000..eccf47febd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -0,0 +1,156 @@ + + + 4.0.0 + cn.iocoder.boot + yudao-module-iot-http-plugin + 0.0.1 + jar + + ${project.artifactId} + 物联网 模块 - http 插件 + + + + http-plugin + cn.iocoder.yudao.module.iot.plugin.HttpPlugin + 0.0.1 + ahh + + + + 17 + ${java.version} + ${java.version} + 1.6 + 2.3 + 2.4 + 0.9.0 + + 1.18.34 + 3.3.1 + UTF-8 + + + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + provided + + + + org.pf4j + pf4j-spring + ${pf4j-spring.version} + provided + + + + cn.iocoder.boot + yudao-module-iot-plugin-api + ${project.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + ${maven-antrun-plugin.version} + + + unzip jar file + package + + + + + + + run + + + + + + + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + + src/main/assembly/assembly.xml + + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.dependencies} + + + + + + + maven-deploy-plugin + + true + + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java new file mode 100644 index 0000000000..32926be452 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.iocoder.yudao.module.iot.api.Greeting; +import com.sun.net.httpserver.HttpServer; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.pf4j.Extension; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * 一个启动 HTTP 服务器的简单插件。 + */ +@Slf4j +public class HttpPlugin extends Plugin { + + private HttpServer server; + + public HttpPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + log.info("HttpPlugin.start()"); + // for testing the development mode + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + log.info("HttpPlugin in DEVELOPMENT mode"); + } + startHttpServer(); + } + + @Override + public void stop() { + log.info("HttpPlugin.stop()"); + stopHttpServer(); + } + + private void startHttpServer() { + try { + server = HttpServer.create(new InetSocketAddress(9081), 0); + server.createContext("/", exchange -> { + String response = "Welcome to PF4J HTTP Server"; + exchange.sendResponseHeaders(200, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + }); + server.setExecutor(null); + server.start(); + log.info("HTTP server started on port 9081"); + } catch (IOException e) { + log.error("Error starting HTTP server", e); + } + } + + private void stopHttpServer() { + if (server != null) { + server.stop(0); + log.info("HTTP server stopped"); + } + } + + @Extension + public static class WelcomeGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Welcome to PF4J"; + } + + } + +} \ No newline at end of file From 10da1e095be48e839851a3c74c4352ced9999e13 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Tue, 17 Dec 2024 16:53:42 +0800 Subject: [PATCH 036/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IOT:=20=E4=BA=A7=E5=93=81=E7=89=A9=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E4=BB=A3=E7=A0=81=E8=AF=84=E5=AE=A1=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotProductThinkModelAccessModeEnum.java} | 4 +- .../IotProductThinkModelTypeEnum.java} | 10 +- .../IotProductThingModelController.java | 84 -------- .../dataType/ThingModelArgument.java | 18 -- .../IotProductThingModelController.http | 28 +-- .../IotProductThinkModelController.java | 84 ++++++++ .../model/ThinkModelEvent.java} | 8 +- .../model/ThinkModelProperty.java} | 14 +- .../model/ThinkModelRespVO.java} | 10 +- .../model/ThinkModelService.java} | 10 +- .../model/dataType/ThinkModelArgument.java | 17 ++ .../dataType/ThinkModelArrayDataSpecs.java} | 6 +- .../ThinkModelBoolOrEnumDataSpecs.java} | 4 +- .../model/dataType/ThinkModelDataSpecs.java} | 24 +-- .../ThinkModelDateOrTextDataSpecs.java} | 4 +- .../dataType/ThinkModelNumericDataSpec.java} | 4 +- .../dataType/ThinkModelStructDataSpecs.java} | 12 +- .../vo/IotProductThinkModelPageReqVO.java} | 8 +- .../vo/IotProductThinkModelRespVO.java} | 16 +- .../vo/IotProductThinkModelSaveReqVO.java} | 20 +- .../convert/device/IotDeviceDataConvert.java | 2 +- .../IotProductThingModelConvert.java | 62 ------ .../IotProductThinkModelConvert.java | 62 ++++++ .../dataobject/device/IotDeviceDataDO.java | 10 +- .../dal/dataobject/tdengine/FieldParser.java | 11 +- ...delMessage.java => ThinkModelMessage.java} | 2 +- .../IotProductThinkModelDO.java} | 26 +-- .../IotProductThinkModelMapper.java | 62 ++++++ .../IotProductThingModelMapper.java | 62 ------ .../device/IotDeviceDataServiceImpl.java | 22 +- .../product/IotProductServiceImpl.java | 4 +- .../tdengine/IotSuperTableService.java | 4 +- .../tdengine/IotSuperTableServiceImpl.java | 34 ++-- ....java => IotThinkModelMessageService.java} | 6 +- ...a => IotThinkModelMessageServiceImpl.java} | 44 ++-- .../IotProductThinkModelService.java} | 24 +-- .../IotProductThinkModelServiceImpl.java} | 188 +++++++++--------- 37 files changed, 504 insertions(+), 506 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/{thingmodel/IotProductThingModelAccessModeEnum.java => thinkmodel/IotProductThinkModelAccessModeEnum.java} (68%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/{thingmodel/IotProductThingModelTypeEnum.java => thinkmodel/IotProductThinkModelTypeEnum.java} (73%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel => thinkmodel}/IotProductThingModelController.http (82%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/ThingModelEvent.java => thinkmodel/model/ThinkModelEvent.java} (58%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/ThingModelProperty.java => thinkmodel/model/ThinkModelProperty.java} (68%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/ThingModelRespVO.java => thinkmodel/model/ThinkModelRespVO.java} (66%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/ThingModelService.java => thinkmodel/model/ThinkModelService.java} (53%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java => thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java} (79%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java => thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java} (82%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java => thinkmodel/model/dataType/ThinkModelDataSpecs.java} (50%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java => thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java} (81%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java => thinkmodel/model/dataType/ThinkModelNumericDataSpec.java} (89%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java => thinkmodel/model/dataType/ThinkModelStructDataSpecs.java} (74%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/vo/IotProductThingModelPageReqVO.java => thinkmodel/vo/IotProductThinkModelPageReqVO.java} (76%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/vo/IotProductThingModelRespVO.java => thinkmodel/vo/IotProductThinkModelRespVO.java} (75%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{productthingmodel/vo/IotProductThingModelSaveReqVO.java => thinkmodel/vo/IotProductThinkModelSaveReqVO.java} (70%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/{ThingModelMessage.java => ThinkModelMessage.java} (97%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/{productthingmodel/IotProductThingModelDO.java => thinkmodel/IotProductThinkModelDO.java} (66%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{IotThingModelMessageService.java => IotThinkModelMessageService.java} (66%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{IotThingModelMessageServiceImpl.java => IotThinkModelMessageServiceImpl.java} (86%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{productthingmodel/IotProductThingModelService.java => thinkmodel/IotProductThinkModelService.java} (58%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{productthingmodel/IotProductThingModelServiceImpl.java => thinkmodel/IotProductThinkModelServiceImpl.java} (66%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelAccessModeEnum.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelAccessModeEnum.java index a755018034..b7f6ca312d 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelAccessModeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.enums.thingmodel; +package cn.iocoder.yudao.module.iot.enums.thinkmodel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,7 +10,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotProductThingModelAccessModeEnum { +public enum IotProductThinkModelAccessModeEnum { READ_ONLY("r"), READ_WRITE("rw"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java similarity index 73% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java index a12299e993..7c7562e279 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.enums.thingmodel; +package cn.iocoder.yudao.module.iot.enums.thinkmodel; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; @@ -14,13 +14,13 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum IotProductThingModelTypeEnum implements IntArrayValuable { +public enum IotProductThinkModelTypeEnum implements IntArrayValuable { PROPERTY(1, "属性"), SERVICE(2, "服务"), EVENT(3, "事件"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThingModelTypeEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThinkModelTypeEnum::getType).toArray(); /** * 类型 @@ -31,8 +31,8 @@ public enum IotProductThingModelTypeEnum implements IntArrayValuable { */ private final String description; - public static IotProductThingModelTypeEnum valueOfType(Integer type) { - for (IotProductThingModelTypeEnum value : values()) { + public static IotProductThinkModelTypeEnum valueOfType(Integer type) { + for (IotProductThinkModelTypeEnum value : values()) { if (value.getType().equals(type)) { return value; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java deleted file mode 100644 index 11a2ffcc50..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.productthingmodel.IotProductThingModelConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 产品物模型") -@RestController -@RequestMapping("/iot/product-thing-model") -@Validated -public class IotProductThingModelController { - - @Resource - private IotProductThingModelService thinkModelFunctionService; - - @PostMapping("/create") - @Operation(summary = "创建产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:create')") - public CommonResult createProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO createReqVO) { - return success(thinkModelFunctionService.createProductThingModel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:update')") - public CommonResult updateProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO updateReqVO) { - thinkModelFunctionService.updateProductThingModel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:delete')") - public CommonResult deleteProductThingModel(@RequestParam("id") Long id) { - thinkModelFunctionService.deleteProductThingModel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult getProductThingModel(@RequestParam("id") Long id) { - IotProductThingModelDO function = thinkModelFunctionService.getProductThingModel(id); - return success(IotProductThingModelConvert.INSTANCE.convert(function)); - } - - @GetMapping("/list-by-product-id") - @Operation(summary = "获得产品物模型") - @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult> getProductThingModelListByProductId(@RequestParam("productId") Long productId) { - List list = thinkModelFunctionService.getProductThingModelListByProductId(productId); - return success(IotProductThingModelConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @Operation(summary = "获得产品物模型分页") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult> getProductThingModelPage(@Valid IotProductThingModelPageReqVO pageReqVO) { - PageResult pageResult = thinkModelFunctionService.getProductThingModelPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotProductThingModelRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java deleted file mode 100644 index 289bb60108..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArgument.java +++ /dev/null @@ -1,18 +0,0 @@ -// TODO @puhui999:productthingmodel 是不是不要这层包了 -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; - -import lombok.Data; - -@Data -public class ThingModelArgument { - - private String identifier; - private String name; - private ThingModelDataSpecs dataType; - /** - * 用于区分输入或输出参数,"input" 或 "output" - */ - private String direction; - private String description; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThingModelController.http similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThingModelController.http index 7742705ef1..2343eed38c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/IotProductThingModelController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThingModelController.http @@ -1,5 +1,5 @@ -### 请求 /iot/product-thing-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-thing-model/create +### 请求 /iot/product-think-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-think-model/create Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -30,8 +30,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-thing-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-thing-model/create +### 请求 /iot/product-think-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-think-model/create Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -65,8 +65,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-thing-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-thing-model/create +### 请求 /iot/product-think-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-think-model/create Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -134,8 +134,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-thing-model/update 接口 => 成功 -PUT {{baseUrl}}/iot/product-thing-model/update +### 请求 /iot/product-think-model/update 接口 => 成功 +PUT {{baseUrl}}/iot/product-think-model/update Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -170,18 +170,18 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-thing-model/delete 接口 => 成功 -DELETE {{baseUrl}}/iot/product-thing-model/delete?id=36 +### 请求 /iot/product-think-model/delete 接口 => 成功 +DELETE {{baseUrl}}/iot/product-think-model/delete?id=36 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} -### 请求 /iot/product-thing-model/get 接口 => 成功 -GET {{baseUrl}}/iot/product-thing-model/get?id=40 +### 请求 /iot/product-think-model/get 接口 => 成功 +GET {{baseUrl}}/iot/product-think-model/get?id=40 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} -### 请求 /iot/product-thing-model/list-by-product-id 接口 => 成功 -GET {{baseUrl}}/iot/product-thing-model/list-by-product-id?productId=1001 +### 请求 /iot/product-think-model/list-by-product-id 接口 => 成功 +GET {{baseUrl}}/iot/product-think-model/list-by-product-id?productId=1001 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java new file mode 100644 index 0000000000..03408b59db --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thinkmodel.IotProductThinkModelConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 产品物模型") +@RestController +@RequestMapping("/iot/product-think-model") +@Validated +public class IotProductThinkModelController { + + @Resource + private IotProductThinkModelService thinkModelFunctionService; + + @PostMapping("/create") + @Operation(summary = "创建产品物模型") + @PreAuthorize("@ss.hasPermission('iot:product-think-model:create')") + public CommonResult createProductThinkModel(@Valid @RequestBody IotProductThinkModelSaveReqVO createReqVO) { + return success(thinkModelFunctionService.createProductThinkModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品物模型") + @PreAuthorize("@ss.hasPermission('iot:product-think-model:update')") + public CommonResult updateProductThinkModel(@Valid @RequestBody IotProductThinkModelSaveReqVO updateReqVO) { + thinkModelFunctionService.updateProductThinkModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:product-think-model:delete')") + public CommonResult deleteProductThinkModel(@RequestParam("id") Long id) { + thinkModelFunctionService.deleteProductThinkModel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:product-think-model:query')") + public CommonResult getProductThinkModel(@RequestParam("id") Long id) { + IotProductThinkModelDO function = thinkModelFunctionService.getProductThinkModel(id); + return success(IotProductThinkModelConvert.INSTANCE.convert(function)); + } + + @GetMapping("/list-by-product-id") + @Operation(summary = "获得产品物模型") + @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:product-think-model:query')") + public CommonResult> getProductThinkModelListByProductId(@RequestParam("productId") Long productId) { + List list = thinkModelFunctionService.getProductThinkModelListByProductId(productId); + return success(IotProductThinkModelConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品物模型分页") + @PreAuthorize("@ss.hasPermission('iot:product-think-model:query')") + public CommonResult> getProductThinkModelPage(@Valid IotProductThinkModelPageReqVO pageReqVO) { + PageResult pageResult = thinkModelFunctionService.getProductThinkModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotProductThinkModelRespVO.class)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelEvent.java similarity index 58% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelEvent.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelEvent.java index 11563dfa86..7d16aa50ba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelEvent.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArgument; import lombok.Data; import java.util.List; @Data -public class ThingModelEvent { +public class ThinkModelEvent { /** * 事件标识符 @@ -26,7 +26,7 @@ public class ThingModelEvent { * "info"、"alert"、"error" */ private String type; - private List outputData; + private List outputData; private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelProperty.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelProperty.java index b07d00c946..a4d2e98921 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelProperty.java @@ -1,7 +1,7 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelAccessModeEnum; import lombok.Data; import java.util.List; @@ -14,7 +14,7 @@ import java.util.List; * @author HUIHUI */ @Data -public class ThingModelProperty { +public class ThinkModelProperty { /** * 属性标识符 @@ -30,7 +30,7 @@ public class ThingModelProperty { private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * 关联枚举 {@link IotProductThinkModelAccessModeEnum} */ private String accessMode; /** @@ -47,10 +47,10 @@ public class ThingModelProperty { /** * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 */ - private ThingModelDataSpecs dataSpecs; + private ThinkModelDataSpecs dataSpecs; /** * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 */ - private List dataSpecsList; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelRespVO.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelRespVO.java index eeb0937bfb..7eb06ce394 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; import lombok.*; @@ -9,7 +9,7 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor @ToString -public class ThingModelRespVO { +public class ThinkModelRespVO { /** * 产品编号 @@ -35,16 +35,16 @@ public class ThingModelRespVO { /** * 属性列表 */ - private List properties; + private List properties; /** * 服务列表 */ - private List services; + private List services; /** * 事件列表 */ - private List events; + private List events; } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelService.java similarity index 53% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelService.java index dfcc8a64b6..851df36564 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelService.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArgument; import lombok.Data; import java.util.List; @Data -public class ThingModelService { +public class ThinkModelService { /** * 服务标识符 @@ -26,8 +26,8 @@ public class ThingModelService { * "sync"、"async" */ private String callType; - private List inputData; - private List outputData; + private List inputData; + private List outputData; private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java new file mode 100644 index 0000000000..9192fc28d9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; + +import lombok.Data; + +@Data +public class ThinkModelArgument { + + private String identifier; + private String name; + private ThinkModelDataSpecs dataType; + /** + * 用于区分输入或输出参数,"input" 或 "output" + */ + private String direction; + private String description; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java similarity index 79% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java index 3be26e296b..5f3e2e8b47 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelArrayDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import java.util.List; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { +public class ThinkModelArrayDataSpecs extends ThinkModelDataSpecs { /** * 数组中的元素个数。 @@ -28,7 +28,7 @@ public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中。 * 此时 struct 取值范围为:int、float、double、text、date、enum、bool */ - private List dataSpecsList; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java index e2ab9bdbb7..8a44b584cd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelBoolOrEnumDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs { +public class ThinkModelBoolOrEnumDataSpecs extends ThinkModelDataSpecs { /** * 枚举项的名称。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDataSpecs.java similarity index 50% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDataSpecs.java index 32090f64bf..2d01747651 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDataSpecs.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; /** - * 抽象类 ThingModelDataSpecs + * 抽象类 ThinkModelDataSpecs * * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类。 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 @@ -15,17 +15,17 @@ import lombok.Data; @Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "dataType", visible = true) @JsonSubTypes({ - @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "int"), - @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "float"), - @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "double"), - @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "text"), - @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "date"), - @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "bool"), - @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "enum"), - @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = "array"), - @JsonSubTypes.Type(value = ThingModelStructDataSpecs.class, name = "struct") + @JsonSubTypes.Type(value = ThinkModelNumericDataSpec.class, name = "int"), + @JsonSubTypes.Type(value = ThinkModelNumericDataSpec.class, name = "float"), + @JsonSubTypes.Type(value = ThinkModelNumericDataSpec.class, name = "double"), + @JsonSubTypes.Type(value = ThinkModelDateOrTextDataSpecs.class, name = "text"), + @JsonSubTypes.Type(value = ThinkModelDateOrTextDataSpecs.class, name = "date"), + @JsonSubTypes.Type(value = ThinkModelBoolOrEnumDataSpecs.class, name = "bool"), + @JsonSubTypes.Type(value = ThinkModelBoolOrEnumDataSpecs.class, name = "enum"), + @JsonSubTypes.Type(value = ThinkModelArrayDataSpecs.class, name = "array"), + @JsonSubTypes.Type(value = ThinkModelStructDataSpecs.class, name = "struct") }) -public abstract class ThingModelDataSpecs { +public abstract class ThinkModelDataSpecs { /** * 数据类型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java similarity index 81% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java index 4ce7d2dee9..640316b47c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelDateOrTextDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { +public class ThinkModelDateOrTextDataSpecs extends ThinkModelDataSpecs { /** * 数据长度,单位为字节。取值不能超过 2048。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelNumericDataSpec.java similarity index 89% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelNumericDataSpec.java index 391cbf90aa..714b57bd6a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelNumericDataSpec.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelNumericDataSpec.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThingModelNumericDataSpec extends ThingModelDataSpecs { +public class ThinkModelNumericDataSpec extends ThinkModelDataSpecs { /** * 最大值,需转为字符串类型。值必须与 dataType 类型一致。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelStructDataSpecs.java similarity index 74% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelStructDataSpecs.java index 04c38ed286..8cbb9afe5b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/thingmodel/dataType/ThingModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelStructDataSpecs.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelAccessModeEnum; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,7 +15,7 @@ import java.util.List; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThingModelStructDataSpecs extends ThingModelDataSpecs { +public class ThinkModelStructDataSpecs extends ThinkModelDataSpecs { /** * 属性标识符 @@ -31,7 +31,7 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * 关联枚举 {@link IotProductThinkModelAccessModeEnum} */ private String accessMode; /** @@ -48,11 +48,11 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { /** * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 */ - private ThingModelDataSpecs dataSpecs; + private ThinkModelDataSpecs dataSpecs; /** * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 */ - private List dataSpecsList; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelPageReqVO.java similarity index 76% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelPageReqVO.java index 31c5ba1d99..afb412228f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelPageReqVO.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -13,7 +13,7 @@ import lombok.ToString; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IotProductThingModelPageReqVO extends PageParam { +public class IotProductThinkModelPageReqVO extends PageParam { @Schema(description = "功能标识") private String identifier; @@ -22,7 +22,7 @@ public class IotProductThingModelPageReqVO extends PageParam { private String name; @Schema(description = "功能类型", example = "1") - @InEnum(IotProductThingModelTypeEnum.class) + @InEnum(IotProductThinkModelTypeEnum.class) private Integer type; @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelRespVO.java similarity index 75% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelRespVO.java index 83c51fb592..a79cd26779 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelRespVO.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated -public class IotProductThingModelRespVO { +public class IotProductThinkModelRespVO { @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") @ExcelProperty("产品ID") @@ -39,13 +39,13 @@ public class IotProductThingModelRespVO { private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelProperty property; + private ThinkModelProperty property; @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelEvent event; + private ThinkModelEvent event; @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelService service; + private ThinkModelService service; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelSaveReqVO.java similarity index 70% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelSaveReqVO.java index e230c39186..3cce60427d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/productthingmodel/vo/IotProductThingModelSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelSaveReqVO.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo; +package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -12,7 +12,7 @@ import lombok.Data; @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data -public class IotProductThingModelSaveReqVO { +public class IotProductThinkModelSaveReqVO { @Schema(description = "编号", example = "1") private Long id; @@ -38,16 +38,16 @@ public class IotProductThingModelSaveReqVO { @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "功能类型不能为空") - @InEnum(IotProductThingModelTypeEnum.class) + @InEnum(IotProductThinkModelTypeEnum.class) private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelProperty property; + private ThinkModelProperty property; @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelService service; + private ThinkModelService service; @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) - private ThingModelEvent event; + private ThinkModelEvent event; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java index d8f6e47042..22d5dda6dc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java @@ -12,7 +12,7 @@ public interface IotDeviceDataConvert { // default List convert(Map deviceData, IotDeviceDO device){ // List list = new ArrayList<>(); // deviceData.forEach((identifier, value) -> { -//// ThingModelProperty property = ThingModelService.INSTANCE.getProperty(device.getProductId(), identifier); +//// ThinkModelProperty property = ThinkModelService.INSTANCE.getProperty(device.getProductId(), identifier); //// if (Objects.isNull(property)) { //// return; //// } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java deleted file mode 100644 index 1d57982d56..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/productthingmodel/IotProductThingModelConvert.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.convert.productthingmodel; - -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; - -import java.util.List; -import java.util.Objects; - -@Mapper -public interface IotProductThingModelConvert { - - IotProductThingModelConvert INSTANCE = Mappers.getMapper(IotProductThingModelConvert.class); - - // 将 SaveReqVO 转换为 DO - @Mapping(target = "property", expression = "java(convertToProperty(bean))") - @Mapping(target = "event", expression = "java(convertToEvent(bean))") - @Mapping(target = "service", expression = "java(convertToService(bean))") - IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); - - // 将 DO 转换为 RespVO - @Mapping(target = "property", source = "property") - @Mapping(target = "event", source = "event") - @Mapping(target = "service", source = "service") - IotProductThingModelRespVO convert(IotProductThingModelDO bean); - - // 批量转换 - List convertList(List list); - - @Named("convertToProperty") - default ThingModelProperty convertToProperty(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - return bean.getProperty(); - } - return null; - } - - @Named("convertToEvent") - default ThingModelEvent convertToEvent(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { - return bean.getEvent(); - } - return null; - } - - @Named("convertToService") - default ThingModelService convertToService(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { - return bean.getService(); - } - return null; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java new file mode 100644 index 0000000000..bb5c22b356 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.convert.thinkmodel; + +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Objects; + +@Mapper +public interface IotProductThinkModelConvert { + + IotProductThinkModelConvert INSTANCE = Mappers.getMapper(IotProductThinkModelConvert.class); + + // 将 SaveReqVO 转换为 DO + @Mapping(target = "property", expression = "java(convertToProperty(bean))") + @Mapping(target = "event", expression = "java(convertToEvent(bean))") + @Mapping(target = "service", expression = "java(convertToService(bean))") + IotProductThinkModelDO convert(IotProductThinkModelSaveReqVO bean); + + // 将 DO 转换为 RespVO + @Mapping(target = "property", source = "property") + @Mapping(target = "event", source = "event") + @Mapping(target = "service", source = "service") + IotProductThinkModelRespVO convert(IotProductThinkModelDO bean); + + // 批量转换 + List convertList(List list); + + @Named("convertToProperty") + default ThinkModelProperty convertToProperty(IotProductThinkModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { + return bean.getProperty(); + } + return null; + } + + @Named("convertToEvent") + default ThinkModelEvent convertToEvent(IotProductThinkModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotProductThinkModelTypeEnum.EVENT.getType())) { + return bean.getEvent(); + } + return null; + } + + @Named("convertToService") + default ThinkModelService convertToService(IotProductThinkModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotProductThinkModelTypeEnum.SERVICE.getType())) { + return bean.getService(); + } + return null; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index 73e7ee34d8..8ff9b13527 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -30,7 +30,7 @@ public class IotDeviceDataDO { /** * 物模型编号 *

- * 关联 {@link IotProductThingModelDO#getId()} + * 关联 {@link IotProductThinkModelDO#getId()} */ private Long thinkModelFunctionId; @@ -51,21 +51,21 @@ public class IotDeviceDataDO { /** * 属性标识符 *

- * 关联 {@link IotProductThingModelDO#getIdentifier()} + * 关联 {@link IotProductThinkModelDO#getIdentifier()} */ private String identifier; /** * 属性名称 *

- * 关联 {@link IotProductThingModelDO#getName()} + * 关联 {@link IotProductThinkModelDO#getName()} */ private String name; /** * 数据类型 *

- * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} + * 关联 {@link IotProductThinkModelDO#getProperty()#getDataType()} */ private String dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 6dfe54c073..065d9bfedf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -1,8 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelRespVO; import java.util.HashMap; import java.util.List; @@ -34,10 +33,10 @@ public class FieldParser { * @param property 物模型属性 * @return TdField对象 */ - public static TdFieldDO parse(ThingModelProperty property) { + public static TdFieldDO parse(ThinkModelProperty property) { String fieldName = property.getIdentifier().toLowerCase(); //// TODO @puhui999: 需要重构 - //ThingModelDataSpecs type = property.getDataType(); + //ThinkModelDataSpecs type = property.getDataType(); // //// 将物模型字段类型映射为td字段类型 //String fType = TYPE_MAPPING.get(type.getDataType().toUpperCase()); @@ -57,7 +56,7 @@ public class FieldParser { * @param thingModel 物模型响应对象 * @return 字段列表 */ - public static List parse(ThingModelRespVO thingModel) { + public static List parse(ThinkModelRespVO thingModel) { return thingModel.getModel().getProperties().stream() .map(FieldParser::parse) .collect(Collectors.toList()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessage.java similarity index 97% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessage.java index d5009dc244..22cf9e2ef6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessage.java @@ -15,7 +15,7 @@ import java.util.Map; @NoArgsConstructor @AllArgsConstructor @Builder -public class ThingModelMessage { +public class ThinkModelMessage { /** * 消息ID diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodel/IotProductThinkModelDO.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodel/IotProductThinkModelDO.java index 1cececc0cc..76b2a63e73 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/productthingmodel/IotProductThingModelDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodel/IotProductThinkModelDO.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel; +package cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -19,17 +19,17 @@ import lombok.NoArgsConstructor; /** * IoT 产品物模型功能 DO *

- * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 + * 每个 {@link IotProductDO} 和 {@link IotProductThinkModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 * * @author 芋道源码 */ -@TableName(value = "iot_product_thing_model", autoResultMap = true) -@KeySequence("iot_product_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName(value = "iot_product_think_model", autoResultMap = true) +@KeySequence("iot_product_think_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class IotProductThingModelDO extends BaseDO { +public class IotProductThinkModelDO extends BaseDO { /** * 物模型功能编号 @@ -66,7 +66,7 @@ public class IotProductThingModelDO extends BaseDO { /** * 功能类型 *

- * 枚举 {@link IotProductThingModelTypeEnum} + * 枚举 {@link IotProductThinkModelTypeEnum} */ private Integer type; @@ -74,18 +74,18 @@ public class IotProductThingModelDO extends BaseDO { * 属性 */ @TableField(typeHandler = JacksonTypeHandler.class) - private ThingModelProperty property; + private ThinkModelProperty property; /** * 事件 */ @TableField(typeHandler = JacksonTypeHandler.class) - private ThingModelEvent event; + private ThinkModelEvent event; /** * 服务 */ @TableField(typeHandler = JacksonTypeHandler.class) - private ThingModelService service; + private ThinkModelService service; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java new file mode 100644 index 0000000000..de76aa6336 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.thinkmodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IoT 产品物模型 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotProductThinkModelMapper extends BaseMapperX { + + default PageResult selectPage(IotProductThinkModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotProductThinkModelDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(IotProductThinkModelDO::getName, reqVO.getName()) + .eqIfPresent(IotProductThinkModelDO::getType, reqVO.getType()) + .eqIfPresent(IotProductThinkModelDO::getProductId, reqVO.getProductId()) + .notIn(IotProductThinkModelDO::getIdentifier, "get", "set", "post") + .orderByDesc(IotProductThinkModelDO::getId)); + } + + default IotProductThinkModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { + return selectOne(IotProductThinkModelDO::getProductId, productId, + IotProductThinkModelDO::getIdentifier, identifier); + } + + default List selectListByProductId(Long productId) { + return selectList(IotProductThinkModelDO::getProductId, productId); + } + + default List selectListByProductIdAndType(Long productId, Integer type) { + return selectList(IotProductThinkModelDO::getProductId, productId, + IotProductThinkModelDO::getType, type); + } + + default List selectListByProductIdAndIdentifiersAndTypes(Long productId, + List identifiers, + List types) { + return selectList(new LambdaQueryWrapperX() + .eq(IotProductThinkModelDO::getProductId, productId) + .in(IotProductThinkModelDO::getIdentifier, identifiers) + .in(IotProductThinkModelDO::getType, types)); + } + + default IotProductThinkModelDO selectByProductIdAndName(Long productId, String name) { + return selectOne(IotProductThinkModelDO::getProductId, productId, + IotProductThinkModelDO::getName, name); + } + + default List selectListByProductKey(String productKey) { + return selectList(IotProductThinkModelDO::getProductKey, productKey); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java deleted file mode 100644 index 6ec0d48463..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodelfunction/IotProductThingModelMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 产品物模型 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotProductThingModelMapper extends BaseMapperX { - - default PageResult selectPage(IotProductThingModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) - .eqIfPresent(IotProductThingModelDO::getType, reqVO.getType()) - .eqIfPresent(IotProductThingModelDO::getProductId, reqVO.getProductId()) - .notIn(IotProductThingModelDO::getIdentifier, "get", "set", "post") - .orderByDesc(IotProductThingModelDO::getId)); - } - - default IotProductThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { - return selectOne(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getIdentifier, identifier); - } - - default List selectListByProductId(Long productId) { - return selectList(IotProductThingModelDO::getProductId, productId); - } - - default List selectListByProductIdAndType(Long productId, Integer type) { - return selectList(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getType, type); - } - - default List selectListByProductIdAndIdentifiersAndTypes(Long productId, - List identifiers, - List types) { - return selectList(new LambdaQueryWrapperX() - .eq(IotProductThingModelDO::getProductId, productId) - .in(IotProductThingModelDO::getIdentifier, identifiers) - .in(IotProductThingModelDO::getType, types)); - } - - default IotProductThingModelDO selectByProductIdAndName(Long productId, String name) { - return selectOne(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getName, name); - } - - default List selectListByProductKey(String productKey) { - return selectList(IotProductThingModelDO::getProductKey, productKey); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 6dbcf0ab7c..ed2e32f2d6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -7,15 +7,15 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.service.tdengine.IotThinkModelMessageService; +import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; @@ -38,9 +38,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @Resource private IotDeviceService deviceService; @Resource - private IotThingModelMessageService thingModelMessageService; + private IotThinkModelMessageService thingModelMessageService; @Resource - private IotProductThingModelService thinkModelFunctionService; + private IotProductThinkModelService thinkModelFunctionService; @Resource private TdEngineDMLMapper tdEngineDMLMapper; @@ -54,7 +54,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 2. 解析消息,保存数据 JSONObject jsonObject = new JSONObject(message); log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject); - ThingModelMessage thingModelMessage = ThingModelMessage.builder() + ThinkModelMessage thingModelMessage = ThinkModelMessage.builder() .id(jsonObject.getStr("id")) .sys(jsonObject.get("sys")) .method(jsonObject.getStr("method")) @@ -64,7 +64,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { .deviceName(deviceName) .deviceKey(device.getDeviceKey()) .build(); - thingModelMessageService.saveThingModelMessage(device, thingModelMessage); + thingModelMessageService.saveThinkModelMessage(device, thingModelMessage); } @Override @@ -73,9 +73,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thinkModelFunctionList = thinkModelFunctionService.getProductThingModelListByProductKey(device.getProductKey()); + List thinkModelFunctionList = thinkModelFunctionService.getProductThinkModelListByProductKey(device.getProductKey()); thinkModelFunctionList = thinkModelFunctionList.stream() - .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType() + .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType() .equals(function.getType())).toList(); // 3. 过滤标识符和属性名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index dabc9ad4a0..b26c2c123b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -34,7 +34,7 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy - private IotProductThingModelService thinkModelFunctionService; + private IotProductThinkModelService thinkModelFunctionService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java index e66278ccd3..bf76ae1c32 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import java.util.List; @@ -14,5 +14,5 @@ public interface IotSuperTableService { /** * 创建超级表数据模型 */ - void createSuperTableDataModel(IotProductDO product, List functionList); + void createSuperTableDataModel(IotProductDO product, List functionList); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java index 84e98296ee..59fa9d0f30 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java @@ -2,15 +2,15 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -33,8 +33,8 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { private String url; @Override - public void createSuperTableDataModel(IotProductDO product, List functionList) { - ThingModelRespVO thingModel = buildThingModel(product, functionList); + public void createSuperTableDataModel(IotProductDO product, List functionList) { + ThinkModelRespVO thingModel = buildThinkModel(product, functionList); if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { log.warn("物模型属性列表为空,不创建超级表"); @@ -56,7 +56,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 创建超级表 */ - private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { + private void createSuperTable(ThinkModelRespVO thingModel, Integer deviceType) { // 解析物模型,获取字段列表 List schemaFields = new ArrayList<>(); schemaFields.add(TdFieldDO.builder() @@ -84,7 +84,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 更新超级表 */ - private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { + private void updateSuperTable(ThinkModelRespVO thingModel, Integer deviceType) { String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); try { List oldFields = getTableFields(superTableName); @@ -210,18 +210,18 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 构建物模型 */ - private ThingModelRespVO buildThingModel(IotProductDO product, List functionList) { - ThingModelRespVO thingModel = new ThingModelRespVO(); + private ThinkModelRespVO buildThinkModel(IotProductDO product, List functionList) { + ThinkModelRespVO thingModel = new ThinkModelRespVO(); thingModel.setId(product.getId()); thingModel.setProductKey(product.getProductKey()); - List properties = functionList.stream() - .filter(function -> IotProductThingModelTypeEnum.PROPERTY.equals( - IotProductThingModelTypeEnum.valueOfType(function.getType()))) - .map(this::buildThingModelProperty) + List properties = functionList.stream() + .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.equals( + IotProductThinkModelTypeEnum.valueOfType(function.getType()))) + .map(this::buildThinkModelProperty) .collect(Collectors.toList()); - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); + ThinkModelRespVO.Model model = new ThinkModelRespVO.Model(); model.setProperties(properties); thingModel.setModel(model); @@ -231,8 +231,8 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 构建物模型属性 */ - private ThingModelProperty buildThingModelProperty(IotProductThingModelDO function) { - ThingModelProperty property = BeanUtil.copyProperties(function, ThingModelProperty.class); + private ThinkModelProperty buildThinkModelProperty(IotProductThinkModelDO function) { + ThinkModelProperty property = BeanUtil.copyProperties(function, ThinkModelProperty.class); property.setDataType(function.getProperty().getDataType()); return property; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java index ffcb3063c5..67ee2b99ae 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; /** * 物模型消息 Service */ -public interface IotThingModelMessageService { +public interface IotThinkModelMessageService { /** * 保存物模型消息 @@ -14,5 +14,5 @@ public interface IotThingModelMessageService { * @param device 设备 * @param thingModelMessage 物模型消息 */ - void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); + void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java index fdb0ca9e42..eaaa3efebd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java @@ -10,16 +10,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.productthingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -33,7 +33,7 @@ import java.util.stream.Collectors; */ @Slf4j @Service -public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { +public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageService { private static final String TAG_NOTE = "TAG"; private static final String NOTE = "note"; @@ -47,7 +47,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private String url; @Resource - private IotProductThingModelService iotProductThingModelService; + private IotProductThinkModelService iotProductThinkModelService; @Resource private IotDeviceService iotDeviceService; @Resource @@ -61,7 +61,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @TenantIgnore - public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { + public void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage) { // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); @@ -71,7 +71,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 Map params = thingModelMessage.dataToMap(); - List functionList = getValidFunctionList(thingModelMessage.getProductKey()); + List functionList = getValidFunctionList(thingModelMessage.getProductKey()); if (functionList.isEmpty()) { return; } @@ -90,21 +90,21 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .build()); } - private List getValidFunctionList(String productKey) { - return iotProductThingModelService - .getProductThingModelListByProductKey(productKey) + private List getValidFunctionList(String productKey) { + return iotProductThinkModelService + .getProductThinkModelListByProductKey(productKey) .stream() - .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) + .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType().equals(function.getType())) .toList(); } - private List filterAndCollectValidFields(Map params, List functionList, IotDeviceDO device, Long time) { + private List filterAndCollectValidFields(Map params, List functionList, IotDeviceDO device, Long time) { // 1. 获取属性标识符集合 - Set propertyIdentifiers = CollectionUtils.convertSet(functionList, IotProductThingModelDO::getIdentifier); + Set propertyIdentifiers = CollectionUtils.convertSet(functionList, IotProductThinkModelDO::getIdentifier); // 2. 构建属性标识符和属性的映射 - Map functionMap = functionList.stream() - .collect(Collectors.toMap(IotProductThingModelDO::getIdentifier, function -> function)); + Map functionMap = functionList.stream() + .collect(Collectors.toMap(IotProductThinkModelDO::getIdentifier, function -> function)); // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); @@ -124,22 +124,22 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * 缓存设备属性 * * @param device 设备信息 - * @param iotProductThingModelDO 物模型属性 + * @param iotProductThinkModelDO 物模型属性 * @param val 属性值 * @param time 时间 */ - private void setDeviceDataCache(IotDeviceDO device, IotProductThingModelDO iotProductThingModelDO, Object val, Long time) { + private void setDeviceDataCache(IotDeviceDO device, IotProductThinkModelDO iotProductThinkModelDO, Object val, Long time) { // TODO @puhui999: 需要重构 IotDeviceDataDO deviceData = IotDeviceDataDO.builder() .productKey(device.getProductKey()) .deviceName(device.getDeviceName()) - .identifier(iotProductThingModelDO.getIdentifier()) + .identifier(iotProductThinkModelDO.getIdentifier()) .value(val != null ? val.toString() : null) .updateTime(DateUtil.toLocalDateTime(new Date(time))) .deviceId(device.getId()) - .thinkModelFunctionId(iotProductThingModelDO.getId()) - .name(iotProductThingModelDO.getName()) - //.dataType(iotProductThingModelDO.getProperty().getDataType().getDataType()) + .thinkModelFunctionId(iotProductThinkModelDO.getId()) + .name(iotProductThinkModelDO.getName()) + //.dataType(iotProductThinkModelDO.getProperty().getDataType().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelService.java similarity index 58% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelService.java index 413b6b0143..34e7fc1498 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelService.java @@ -1,9 +1,9 @@ -package cn.iocoder.yudao.module.iot.service.productthingmodel; +package cn.iocoder.yudao.module.iot.service.thinkmodel; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import jakarta.validation.Valid; import java.util.List; @@ -13,7 +13,7 @@ import java.util.List; * * @author 芋道源码 */ -public interface IotProductThingModelService { +public interface IotProductThinkModelService { /** * 创建产品物模型 @@ -21,21 +21,21 @@ public interface IotProductThingModelService { * @param createReqVO 创建信息 * @return 编号 */ - Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); + Long createProductThinkModel(@Valid IotProductThinkModelSaveReqVO createReqVO); /** * 更新产品物模型 * * @param updateReqVO 更新信息 */ - void updateProductThingModel(@Valid IotProductThingModelSaveReqVO updateReqVO); + void updateProductThinkModel(@Valid IotProductThinkModelSaveReqVO updateReqVO); /** * 删除产品物模型 * * @param id 编号 */ - void deleteProductThingModel(Long id); + void deleteProductThinkModel(Long id); /** * 获得产品物模型 @@ -43,7 +43,7 @@ public interface IotProductThingModelService { * @param id 编号 * @return 产品物模型 */ - IotProductThingModelDO getProductThingModel(Long id); + IotProductThinkModelDO getProductThinkModel(Long id); /** * 获得产品物模型列表 @@ -51,7 +51,7 @@ public interface IotProductThingModelService { * @param productId 产品编号 * @return 产品物模型列表 */ - List getProductThingModelListByProductId(Long productId); + List getProductThinkModelListByProductId(Long productId); /** * 获得产品物模型分页 @@ -59,7 +59,7 @@ public interface IotProductThingModelService { * @param pageReqVO 分页查询 * @return 产品物模型分页 */ - PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); + PageResult getProductThinkModelPage(IotProductThinkModelPageReqVO pageReqVO); /** * 创建超级表数据模型 @@ -74,6 +74,6 @@ public interface IotProductThingModelService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getProductThingModelListByProductKey(String productKey); + List getProductThinkModelListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java index f5efd37bcc..862d8b95c3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/productthingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java @@ -1,23 +1,23 @@ -package cn.iocoder.yudao.module.iot.service.productthingmodel; +package cn.iocoder.yudao.module.iot.service.thinkmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArgument; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelArrayDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.thingmodel.dataType.ThingModelDateOrTextDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.productthingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.productthingmodel.IotProductThingModelConvert; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArrayDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelDateOrTextDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thinkmodel.IotProductThinkModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.productthingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotProductThingModelMapper; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodel.IotProductThinkModelMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; @@ -41,10 +41,10 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j -public class IotProductThingModelServiceImpl implements IotProductThingModelService { +public class IotProductThinkModelServiceImpl implements IotProductThinkModelService { @Resource - private IotProductThingModelMapper productThingModelMapper; + private IotProductThinkModelMapper productThinkModelMapper; @Resource private IotProductService productService; @@ -53,7 +53,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Override @Transactional(rollbackFor = Exception.class) - public Long createProductThingModel(IotProductThingModelSaveReqVO createReqVO) { + public Long createProductThinkModel(IotProductThinkModelSaveReqVO createReqVO) { // 1. 校验功能标识符在同一产品下是否唯一 validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); @@ -67,11 +67,11 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ validateProductStatus(createReqVO.getProductId()); // 5. 插入数据库 - IotProductThingModelDO function = IotProductThingModelConvert.INSTANCE.convert(createReqVO); - productThingModelMapper.insert(function); + IotProductThinkModelDO function = IotProductThinkModelConvert.INSTANCE.convert(createReqVO); + productThinkModelMapper.insert(function); // 6. 如果创建的是属性,需要更新默认的事件和服务 - if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(createReqVO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { //createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } return function.getId(); @@ -95,14 +95,14 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } private void validateNameUnique(Long productId, String name) { - IotProductThingModelDO function = productThingModelMapper.selectByProductIdAndName(productId, name); + IotProductThinkModelDO function = productThinkModelMapper.selectByProductIdAndName(productId, name); if (function != null) { throw exception(THINK_MODEL_FUNCTION_NAME_EXISTS); } } private void validateIdentifierUnique(Long productId, String identifier) { - IotProductThingModelDO function = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + IotProductThinkModelDO function = productThinkModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (function != null) { throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); } @@ -110,9 +110,9 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Override @Transactional(rollbackFor = Exception.class) - public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { + public void updateProductThinkModel(IotProductThinkModelSaveReqVO updateReqVO) { // 1. 校验功能是否存在 - validateproductThingModelMapperExists(updateReqVO.getId()); + validateproductThinkModelMapperExists(updateReqVO.getId()); // 2. 校验功能标识符是否唯一 validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); @@ -121,17 +121,17 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ validateProductStatus(updateReqVO.getProductId()); // 4. 更新数据库 - IotProductThingModelDO productThingModelDO = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); - productThingModelMapper.updateById(productThingModelDO); + IotProductThinkModelDO productThinkModelDO = IotProductThinkModelConvert.INSTANCE.convert(updateReqVO); + productThinkModelMapper.updateById(productThinkModelDO); // 5. 如果更新的是属性,需要更新默认的事件和服务 - if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(updateReqVO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } } private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotProductThingModelDO function = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + IotProductThinkModelDO function = productThinkModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (function != null && ObjectUtil.notEqual(function.getId(), id)) { throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); } @@ -139,9 +139,9 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Override @Transactional(rollbackFor = Exception.class) - public void deleteProductThingModel(Long id) { + public void deleteProductThinkModel(Long id) { // 1. 校验功能是否存在 - IotProductThingModelDO functionDO = productThingModelMapper.selectById(id); + IotProductThinkModelDO functionDO = productThinkModelMapper.selectById(id); if (functionDO == null) { throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } @@ -150,10 +150,10 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ validateProductStatus(functionDO.getProductId()); // 2. 删除功能 - productThingModelMapper.deleteById(id); + productThinkModelMapper.deleteById(id); // 3. 如果删除的是属性,需要更新默认的事件和服务 - if (Objects.equals(functionDO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(functionDO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(functionDO.getProductId(), functionDO.getProductKey()); } } @@ -163,25 +163,25 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ * * @param id 功能编号 */ - private void validateproductThingModelMapperExists(Long id) { - if (productThingModelMapper.selectById(id) == null) { + private void validateproductThinkModelMapperExists(Long id) { + if (productThinkModelMapper.selectById(id) == null) { throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } } @Override - public IotProductThingModelDO getProductThingModel(Long id) { - return productThingModelMapper.selectById(id); + public IotProductThinkModelDO getProductThinkModel(Long id) { + return productThinkModelMapper.selectById(id); } @Override - public List getProductThingModelListByProductId(Long productId) { - return productThingModelMapper.selectListByProductId(productId); + public List getProductThinkModelListByProductId(Long productId) { + return productThinkModelMapper.selectListByProductId(productId); } @Override - public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { - return productThingModelMapper.selectPage(pageReqVO); + public PageResult getProductThinkModelPage(IotProductThinkModelPageReqVO pageReqVO) { + return productThinkModelMapper.selectPage(pageReqVO); } @Override @@ -190,15 +190,15 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ IotProductDO product = productService.getProduct(productId); // 2. 查询产品的物模型功能列表 - List functionList = productThingModelMapper.selectListByProductId(productId); + List functionList = productThinkModelMapper.selectListByProductId(productId); // 3. 生成 TDengine 的数据模型 dbStructureDataService.createSuperTableDataModel(product, functionList); } @Override - public List getProductThingModelListByProductKey(String productKey) { - return productThingModelMapper.selectListByProductKey(productKey); + public List getProductThinkModelListByProductKey(String productKey) { + return productThinkModelMapper.selectListByProductKey(productKey); } // TODO @puhui999: 需要重构 @@ -207,82 +207,82 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 - List propertyList = productThingModelMapper - .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); + List propertyList = productThinkModelMapper + .selectListByProductIdAndType(productId, IotProductThinkModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 - List newFunctionList = new ArrayList<>(); + List newFunctionList = new ArrayList<>(); // 生成属性上报事件 - ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); + ThinkModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); if (propertyPostEvent != null) { - IotProductThingModelDO eventFunction = buildEventFunctionDO(productId, productKey, propertyPostEvent); + IotProductThinkModelDO eventFunction = buildEventFunctionDO(productId, productKey, propertyPostEvent); newFunctionList.add(eventFunction); } // 生成属性设置服务 - ThingModelService propertySetService = generatePropertySetService(propertyList); + ThinkModelService propertySetService = generatePropertySetService(propertyList); if (propertySetService != null) { - IotProductThingModelDO setServiceFunction = buildServiceFunctionDO(productId, productKey, propertySetService); + IotProductThinkModelDO setServiceFunction = buildServiceFunctionDO(productId, productKey, propertySetService); newFunctionList.add(setServiceFunction); } // 生成属性获取服务 - ThingModelService propertyGetService = generatePropertyGetService(propertyList); + ThinkModelService propertyGetService = generatePropertyGetService(propertyList); if (propertyGetService != null) { - IotProductThingModelDO getServiceFunction = buildServiceFunctionDO(productId, productKey, propertyGetService); + IotProductThinkModelDO getServiceFunction = buildServiceFunctionDO(productId, productKey, propertyGetService); newFunctionList.add(getServiceFunction); } // 3. 获取数据库中的默认的旧事件和服务列表 - List oldFunctionList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( + List oldFunctionList = productThinkModelMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), - Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) + Arrays.asList(IotProductThinkModelTypeEnum.EVENT.getType(), IotProductThinkModelTypeEnum.SERVICE.getType()) ); // 3.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldFunctionList, newFunctionList, + List> diffResult = diffList(oldFunctionList, newFunctionList, // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier()) && Objects.equals(oldFunc.getType(), newFunc.getType())); - List createList = diffResult.get(0); // 需要新增的 - List updateList = diffResult.get(1); // 需要更新的 - List deleteList = diffResult.get(2); // 需要删除的 + List createList = diffResult.get(0); // 需要新增的 + List updateList = diffResult.get(1); // 需要更新的 + List deleteList = diffResult.get(2); // 需要删除的 // 3.2 批量执行数据库操作 // 新增数据库中的新事件和服务列表 if (CollUtil.isNotEmpty(createList)) { - productThingModelMapper.insertBatch(createList); + productThinkModelMapper.insertBatch(createList); } // 更新数据库中的事件和服务列表 if (CollUtil.isNotEmpty(updateList)) { // 首先,为每个需要更新的对象设置其对应的 ID updateList.forEach(updateFunc -> { - IotProductThingModelDO oldFunc = findFunctionByIdentifierAndType( + IotProductThinkModelDO oldFunc = findFunctionByIdentifierAndType( oldFunctionList, updateFunc.getIdentifier(), updateFunc.getType()); if (oldFunc != null) { updateFunc.setId(oldFunc.getId()); } }); // 过滤掉没有设置 ID 的对象 - List validUpdateList = updateList.stream() + List validUpdateList = updateList.stream() .filter(func -> func.getId() != null) .collect(Collectors.toList()); // 执行批量更新 if (CollUtil.isNotEmpty(validUpdateList)) { - productThingModelMapper.updateBatch(validUpdateList); + productThinkModelMapper.updateBatch(validUpdateList); } } // 删除数据库中的旧事件和服务列表 if (CollUtil.isNotEmpty(deleteList)) { - Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); - productThingModelMapper.deleteByIds(idsToDelete); + Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThinkModelDO::getId); + productThinkModelMapper.deleteByIds(idsToDelete); } } /** * 根据标识符和类型查找功能对象 */ - private IotProductThingModelDO findFunctionByIdentifierAndType(List functionList, + private IotProductThinkModelDO findFunctionByIdentifierAndType(List functionList, String identifier, Integer type) { return CollUtil.findOne(functionList, func -> Objects.equals(func.getIdentifier(), identifier) && Objects.equals(func.getType(), type)); @@ -291,40 +291,40 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 构建事件功能对象 */ - private IotProductThingModelDO buildEventFunctionDO(Long productId, String productKey, ThingModelEvent event) { - return new IotProductThingModelDO() + private IotProductThinkModelDO buildEventFunctionDO(Long productId, String productKey, ThinkModelEvent event) { + return new IotProductThinkModelDO() .setProductId(productId) .setProductKey(productKey) .setIdentifier(event.getIdentifier()) .setName(event.getName()) .setDescription(event.getDescription()) - .setType(IotProductThingModelTypeEnum.EVENT.getType()) + .setType(IotProductThinkModelTypeEnum.EVENT.getType()) .setEvent(event); } /** * 构建服务功能对象 */ - private IotProductThingModelDO buildServiceFunctionDO(Long productId, String productKey, ThingModelService service) { - return new IotProductThingModelDO() + private IotProductThinkModelDO buildServiceFunctionDO(Long productId, String productKey, ThinkModelService service) { + return new IotProductThinkModelDO() .setProductId(productId) .setProductKey(productKey) .setIdentifier(service.getIdentifier()) .setName(service.getName()) .setDescription(service.getDescription()) - .setType(IotProductThingModelTypeEnum.SERVICE.getType()) + .setType(IotProductThinkModelTypeEnum.SERVICE.getType()) .setService(service); } /** * 生成属性上报事件 */ - private ThingModelEvent generatePropertyPostEvent(List propertyList) { + private ThinkModelEvent generatePropertyPostEvent(List propertyList) { if (CollUtil.isEmpty(propertyList)) { return null; } - ThingModelEvent event = new ThingModelEvent() + ThinkModelEvent event = new ThinkModelEvent() .setIdentifier("post") .setName("属性上报") .setType("info") @@ -332,11 +332,11 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ .setMethod("thing.event.property.post"); // 将属性列表转换为事件的输出参数 - List outputData = new ArrayList<>(); + List outputData = new ArrayList<>(); // TODO @puhui999: 需要重构 - for (IotProductThingModelDO functionDO : propertyList) { - ThingModelProperty property = functionDO.getProperty(); - ThingModelArgument arg = new ThingModelArgument() + for (IotProductThinkModelDO functionDO : propertyList) { + ThinkModelProperty property = functionDO.getProperty(); + ThinkModelArgument arg = new ThinkModelArgument() .setIdentifier(property.getIdentifier()) .setName(property.getName()) //.setDataType(property.getDataType()) @@ -351,17 +351,17 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 生成属性设置服务 */ - private ThingModelService generatePropertySetService(List propertyList) { + private ThinkModelService generatePropertySetService(List propertyList) { if (propertyList == null || propertyList.isEmpty()) { return null; } - List inputData = new ArrayList<>(); + List inputData = new ArrayList<>(); // TODO @puhui999: 需要重构 - for (IotProductThingModelDO functionDO : propertyList) { - ThingModelProperty property = functionDO.getProperty(); - //if (IotProductThingModelAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { - // ThingModelArgument arg = new ThingModelArgument() + for (IotProductThinkModelDO functionDO : propertyList) { + ThinkModelProperty property = functionDO.getProperty(); + //if (IotProductThinkModelAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotProductThinkModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { + // ThinkModelArgument arg = new ThinkModelArgument() // .setIdentifier(property.getIdentifier()) // .setName(property.getName()) // .setDataType(property.getDataType()) @@ -376,7 +376,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } // 属性设置服务一般不需要输出参数 - return new ThingModelService() + return new ThinkModelService() .setIdentifier("set") .setName("属性设置") .setCallType("async") @@ -390,17 +390,17 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 生成属性获取服务 */ - private ThingModelService generatePropertyGetService(List propertyList) { + private ThinkModelService generatePropertyGetService(List propertyList) { if (propertyList == null || propertyList.isEmpty()) { return null; } // TODO @puhui999: 需要重构 - List outputData = new ArrayList<>(); - for (IotProductThingModelDO functionDO : propertyList) { - ThingModelProperty property = functionDO.getProperty(); + List outputData = new ArrayList<>(); + for (IotProductThinkModelDO functionDO : propertyList) { + ThinkModelProperty property = functionDO.getProperty(); //if (ObjectUtils.equalsAny(property.getAccessMode(), - // IotProductThingModelAccessModeEnum.READ.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { - // ThingModelArgument arg = new ThingModelArgument() + // IotProductThinkModelAccessModeEnum.READ.getMode(), IotProductThinkModelAccessModeEnum.READ_WRITE.getMode())) { + // ThinkModelArgument arg = new ThinkModelArgument() // .setIdentifier(property.getIdentifier()) // .setName(property.getName()) // .setDataType(property.getDataType()) @@ -414,7 +414,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ return null; } - ThingModelService service = new ThingModelService() + ThinkModelService service = new ThinkModelService() .setIdentifier("get") .setName("属性获取") .setCallType("async") @@ -422,17 +422,17 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ .setMethod("thing.service.property.get"); // 定义输入参数:属性标识符列表 - ThingModelArgument inputArg = new ThingModelArgument() + ThinkModelArgument inputArg = new ThinkModelArgument() .setIdentifier("properties") .setName("属性标识符列表") .setDescription("需要获取的属性标识符列表") .setDirection("input"); // 设置为输入参数 // 创建数组类型,元素类型为文本类型(字符串) - ThingModelArrayDataSpecs arrayType = new ThingModelArrayDataSpecs(); + ThinkModelArrayDataSpecs arrayType = new ThinkModelArrayDataSpecs(); arrayType.setDataType("array"); - //ThingModelArraySpecs arraySpecs = new ThingModelArraySpecs(); - ThingModelDateOrTextDataSpecs textType = new ThingModelDateOrTextDataSpecs(); + //ThinkModelArraySpecs arraySpecs = new ThinkModelArraySpecs(); + ThinkModelDateOrTextDataSpecs textType = new ThinkModelDateOrTextDataSpecs(); textType.setDataType("text"); //arraySpecs.setItem(textType); //arrayType.setSpecs(arraySpecs); From 0352dda469095b49555badf76fe33593a57c3974 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 18 Dec 2024 18:07:49 +0800 Subject: [PATCH 037/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IOT:=20=E4=BC=98=E5=8C=96=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=20TODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/dataType/ThinkModelArgument.java | 6 +- .../dataobject/device/IotDeviceDataDO.java | 2 +- .../dal/dataobject/tdengine/FieldParser.java | 23 +++--- .../device/IotDeviceDataServiceImpl.java | 34 ++++---- .../IotThinkModelMessageServiceImpl.java | 5 +- .../IotProductThinkModelServiceImpl.java | 77 +++++++++---------- 6 files changed, 72 insertions(+), 75 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java index 9192fc28d9..6a223286ac 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; import lombok.Data; @Data @@ -7,7 +8,10 @@ public class ThinkModelArgument { private String identifier; private String name; - private ThinkModelDataSpecs dataType; + /** + * 物模型中的属性 + */ + private ThinkModelProperty property; /** * 用于区分输入或输出参数,"input" 或 "output" */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index 8ff9b13527..c3ed4863ee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -32,7 +32,7 @@ public class IotDeviceDataDO { *

* 关联 {@link IotProductThinkModelDO#getId()} */ - private Long thinkModelFunctionId; + private Long thinkModelId; /** * 产品标识 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 065d9bfedf..81bfed068f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -35,19 +35,16 @@ public class FieldParser { */ public static TdFieldDO parse(ThinkModelProperty property) { String fieldName = property.getIdentifier().toLowerCase(); - //// TODO @puhui999: 需要重构 - //ThinkModelDataSpecs type = property.getDataType(); - // - //// 将物模型字段类型映射为td字段类型 - //String fType = TYPE_MAPPING.get(type.getDataType().toUpperCase()); - // - //// 如果字段类型为NCHAR,默认长度为64 - //int dataLength = 0; - //if ("NCHAR".equals(fType)) { - // dataLength = 64; - //} - //return new TdFieldDO(fieldName, fType, dataLength); - return null; + + // 将物模型字段类型映射为td字段类型 + String fType = TYPE_MAPPING.get(property.getDataType().toUpperCase()); + + // 如果字段类型为NCHAR,默认长度为64 + int dataLength = 0; + if ("NCHAR".equals(fType)) { + dataLength = 64; + } + return new TdFieldDO(fieldName, fType, dataLength); } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index ed2e32f2d6..71780824a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -7,9 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; @@ -28,6 +28,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; + @Slf4j @Service public class IotDeviceDataServiceImpl implements IotDeviceDataService { @@ -73,35 +75,31 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thinkModelFunctionList = thinkModelFunctionService.getProductThinkModelListByProductKey(device.getProductKey()); - thinkModelFunctionList = thinkModelFunctionList.stream() - .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType() - .equals(function.getType())).toList(); + List thinkModelList = thinkModelFunctionService.getProductThinkModelListByProductKey(device.getProductKey()); + thinkModelList = filterList(thinkModelList, thinkModel -> IotProductThinkModelTypeEnum.PROPERTY.getType() + .equals(thinkModel.getType())); // 3. 过滤标识符和属性名称 if (deviceDataReqVO.getIdentifier() != null) { - thinkModelFunctionList = thinkModelFunctionList.stream() - .filter(function -> function.getIdentifier().toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())) - .toList(); + thinkModelList = filterList(thinkModelList, thinkModel -> thinkModel.getIdentifier() + .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())); } if (deviceDataReqVO.getName() != null) { - thinkModelFunctionList = thinkModelFunctionList.stream() - .filter(function -> function.getName().toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())) - .toList(); + thinkModelList = filterList(thinkModelList, thinkModel -> thinkModel.getName() + .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())); } // 4. 获取设备属性最新数据 - // TODO @puhui999: 需要重构 - thinkModelFunctionList.forEach(function -> { - IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), function.getIdentifier()); + thinkModelList.forEach(thinkModel -> { + IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thinkModel.getIdentifier()); if (deviceData == null) { deviceData = new IotDeviceDataDO(); deviceData.setProductKey(device.getProductKey()); deviceData.setDeviceName(device.getDeviceName()); - deviceData.setIdentifier(function.getIdentifier()); + deviceData.setIdentifier(thinkModel.getIdentifier()); deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); - deviceData.setThinkModelFunctionId(function.getId()); - deviceData.setName(function.getName()); - //deviceData.setDataType(function.getProperty().getDataType().getDataType()); + deviceData.setThinkModelId(thinkModel.getId()); + deviceData.setName(thinkModel.getName()); + deviceData.setDataType(thinkModel.getProperty().getDataType()); } list.add(deviceData); }); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java index eaaa3efebd..3c2debfa49 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java @@ -129,7 +129,6 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ * @param time 时间 */ private void setDeviceDataCache(IotDeviceDO device, IotProductThinkModelDO iotProductThinkModelDO, Object val, Long time) { - // TODO @puhui999: 需要重构 IotDeviceDataDO deviceData = IotDeviceDataDO.builder() .productKey(device.getProductKey()) .deviceName(device.getDeviceName()) @@ -137,9 +136,9 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ .value(val != null ? val.toString() : null) .updateTime(DateUtil.toLocalDateTime(new Date(time))) .deviceId(device.getId()) - .thinkModelFunctionId(iotProductThinkModelDO.getId()) + .thinkModelId(iotProductThinkModelDO.getId()) .name(iotProductThinkModelDO.getName()) - //.dataType(iotProductThinkModelDO.getProperty().getDataType().getDataType()) + .dataType(iotProductThinkModelDO.getProperty().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java index 862d8b95c3..fa15083f02 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; @@ -17,6 +18,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodel.IotProductThinkModelMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelAccessModeEnum; import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; @@ -72,7 +74,7 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ // 6. 如果创建的是属性,需要更新默认的事件和服务 if (Objects.equals(createReqVO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { - //createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); + createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } return function.getId(); } @@ -112,7 +114,7 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ @Transactional(rollbackFor = Exception.class) public void updateProductThinkModel(IotProductThinkModelSaveReqVO updateReqVO) { // 1. 校验功能是否存在 - validateproductThinkModelMapperExists(updateReqVO.getId()); + validateProductThinkModelMapperExists(updateReqVO.getId()); // 2. 校验功能标识符是否唯一 validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); @@ -163,7 +165,7 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ * * @param id 功能编号 */ - private void validateproductThinkModelMapperExists(Long id) { + private void validateProductThinkModelMapperExists(Long id) { if (productThinkModelMapper.selectById(id) == null) { throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); } @@ -201,7 +203,6 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ return productThinkModelMapper.selectListByProductKey(productKey); } - // TODO @puhui999: 需要重构 /** * 创建默认的事件和服务 */ @@ -333,14 +334,12 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ // 将属性列表转换为事件的输出参数 List outputData = new ArrayList<>(); - // TODO @puhui999: 需要重构 - for (IotProductThinkModelDO functionDO : propertyList) { - ThinkModelProperty property = functionDO.getProperty(); + for (IotProductThinkModelDO thinkModel : propertyList) { ThinkModelArgument arg = new ThinkModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - //.setDataType(property.getDataType()) - .setDescription(property.getDescription()) + .setIdentifier(thinkModel.getIdentifier()) + .setName(thinkModel.getName()) + .setProperty(thinkModel.getProperty()) + .setDescription(thinkModel.getDescription()) .setDirection("output"); // 设置为输出参数 outputData.add(arg); } @@ -357,18 +356,17 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ } List inputData = new ArrayList<>(); - // TODO @puhui999: 需要重构 - for (IotProductThinkModelDO functionDO : propertyList) { - ThinkModelProperty property = functionDO.getProperty(); - //if (IotProductThinkModelAccessModeEnum.WRITE.getMode().equals(property.getAccessMode()) || IotProductThinkModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { - // ThinkModelArgument arg = new ThinkModelArgument() - // .setIdentifier(property.getIdentifier()) - // .setName(property.getName()) - // .setDataType(property.getDataType()) - // .setDescription(property.getDescription()) - // .setDirection("input"); // 设置为输入参数 - // inputData.add(arg); - //} + for (IotProductThinkModelDO thinkModel : propertyList) { + ThinkModelProperty property = thinkModel.getProperty(); + if (IotProductThinkModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { + ThinkModelArgument arg = new ThinkModelArgument() + .setIdentifier(property.getIdentifier()) + .setName(property.getName()) + .setProperty(property) + .setDescription(property.getDescription()) + .setDirection("input"); // 设置为输入参数 + inputData.add(arg); + } } if (inputData.isEmpty()) { // 如果没有可写属性,不生成属性设置服务 @@ -394,20 +392,20 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ if (propertyList == null || propertyList.isEmpty()) { return null; } - // TODO @puhui999: 需要重构 + List outputData = new ArrayList<>(); for (IotProductThinkModelDO functionDO : propertyList) { ThinkModelProperty property = functionDO.getProperty(); - //if (ObjectUtils.equalsAny(property.getAccessMode(), - // IotProductThinkModelAccessModeEnum.READ.getMode(), IotProductThinkModelAccessModeEnum.READ_WRITE.getMode())) { - // ThinkModelArgument arg = new ThinkModelArgument() - // .setIdentifier(property.getIdentifier()) - // .setName(property.getName()) - // .setDataType(property.getDataType()) - // .setDescription(property.getDescription()) - // .setDirection("output"); // 设置为输出参数 - // outputData.add(arg); - //} + if (ObjectUtils.equalsAny(property.getAccessMode(), + IotProductThinkModelAccessModeEnum.READ_ONLY.getMode(), IotProductThinkModelAccessModeEnum.READ_WRITE.getMode())) { + ThinkModelArgument arg = new ThinkModelArgument() + .setIdentifier(property.getIdentifier()) + .setName(property.getName()) + .setProperty(property) + .setDescription(property.getDescription()) + .setDirection("output"); // 设置为输出参数 + outputData.add(arg); + } } if (outputData.isEmpty()) { // 如果没有可读属性,不生成属性获取服务 @@ -428,15 +426,16 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ .setDescription("需要获取的属性标识符列表") .setDirection("input"); // 设置为输入参数 - // 创建数组类型,元素类型为文本类型(字符串) + // 创建数组类型,元素类型为文本类型(字符串)TODO @puhui999: 还得研究研究 ThinkModelArrayDataSpecs arrayType = new ThinkModelArrayDataSpecs(); arrayType.setDataType("array"); - //ThinkModelArraySpecs arraySpecs = new ThinkModelArraySpecs(); + inputArg.setProperty(new ThinkModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) + .setDescription(inputArg.getDescription()).setDataSpecs(arrayType)); + ThinkModelDateOrTextDataSpecs textType = new ThinkModelDateOrTextDataSpecs(); textType.setDataType("text"); - //arraySpecs.setItem(textType); - //arrayType.setSpecs(arraySpecs); - inputArg.setDataType(arrayType); + inputArg.setProperty(new ThinkModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) + .setDescription(inputArg.getDescription()).setDataSpecs(textType)); service.setInputData(Collections.singletonList(inputArg)); service.setOutputData(outputData); From de78cc9258b03557b8c8246705f60365f7d11643 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 18 Dec 2024 20:41:31 +0800 Subject: [PATCH 038/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E8=AF=84=E5=AE=A1=20ThingModel?= =?UTF-8?q?=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java | 1 - .../admin/thinkmodel/model/dataType/ThinkModelArgument.java | 3 +++ .../thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java index 7c7562e279..0ed00d08f9 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java @@ -6,7 +6,6 @@ import lombok.Getter; import java.util.Arrays; -// TODO @芋艿:纠结下,到底叫 thinkmodel 好,还是 function 好 /** * IOT 产品功能(物模型)类型枚举类 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java index 6a223286ac..7b293ca79e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java @@ -6,6 +6,9 @@ import lombok.Data; @Data public class ThinkModelArgument { + public static final String DIRECTION_INPUT = "input"; + public static final String DIRECTION_OUTPUT = "output"; + private String identifier; private String name; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java index 5f3e2e8b47..ebc69f640b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java @@ -6,6 +6,7 @@ import lombok.EqualsAndHashCode; import java.util.List; +// TODO @puhui999:thingmodel 哈 = = 之前我写错单词了 /** * 物模型数据类型为数组的 DataSpec 定义 * From 8454a10cea33f74c1c82fa74f4e5fac9cd654e31 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 19 Dec 2024 11:19:10 +0800 Subject: [PATCH 039/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IOT:=20ThingModel=20=E8=AF=84=E5=AE=A1?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 12 +- .../IotProductThingModelAccessModeEnum.java} | 4 +- .../IotProductThingModelTypeEnum.java} | 10 +- .../vo/deviceData/IotDeviceDataRespVO.java | 2 +- .../IotProductThingModelController.http | 28 +- .../IotProductThingModelController.java | 84 ++++++ .../model/ThingModelEvent.java} | 8 +- .../model/ThingModelProperty.java} | 14 +- .../model/ThingModelRespVO.java} | 10 +- .../model/ThingModelService.java} | 10 +- .../model/dataType/ThingModelArgument.java} | 8 +- .../dataType/ThingModelArrayDataSpecs.java} | 7 +- .../ThingModelBoolOrEnumDataSpecs.java} | 4 +- .../model/dataType/ThingModelDataSpecs.java} | 24 +- .../ThingModelDateOrTextDataSpecs.java} | 4 +- .../dataType/ThingModelNumericDataSpec.java} | 4 +- .../dataType/ThingModelStructDataSpecs.java} | 12 +- .../vo/IotProductThingModelPageReqVO.java} | 8 +- .../vo/IotProductThingModelRespVO.java} | 16 +- .../vo/IotProductThingModelSaveReqVO.java} | 20 +- .../IotProductThinkModelController.java | 84 ------ .../convert/device/IotDeviceDataConvert.java | 2 +- .../IotProductThingModelConvert.java | 62 +++++ .../IotProductThinkModelConvert.java | 62 ----- .../dataobject/device/IotDeviceDataDO.java | 12 +- .../dal/dataobject/tdengine/FieldParser.java | 8 +- ...delMessage.java => ThingModelMessage.java} | 2 +- .../IotProductThingModelDO.java} | 26 +- .../IotProductThingModelMapper.java | 62 +++++ .../IotProductThinkModelMapper.java | 62 ----- .../device/IotDeviceDataServiceImpl.java | 40 +-- .../product/IotProductServiceImpl.java | 6 +- .../tdengine/IotSuperTableService.java | 5 +- .../tdengine/IotSuperTableServiceImpl.java | 36 +-- ....java => IotThingModelMessageService.java} | 6 +- ...a => IotThingModelMessageServiceImpl.java} | 58 ++--- .../IotProductThingModelService.java} | 24 +- .../IotProductThingModelServiceImpl.java} | 243 +++++++++--------- 38 files changed, 542 insertions(+), 547 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/{thinkmodel/IotProductThinkModelAccessModeEnum.java => thingmodel/IotProductThingModelAccessModeEnum.java} (68%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/{thinkmodel/IotProductThinkModelTypeEnum.java => thingmodel/IotProductThingModelTypeEnum.java} (71%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel => thingmodel}/IotProductThingModelController.http (82%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/ThinkModelEvent.java => thingmodel/model/ThingModelEvent.java} (61%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/ThinkModelProperty.java => thingmodel/model/ThingModelProperty.java} (69%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/ThinkModelRespVO.java => thingmodel/model/ThingModelRespVO.java} (68%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/ThinkModelService.java => thingmodel/model/ThingModelService.java} (56%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelArgument.java => thingmodel/model/dataType/ThingModelArgument.java} (64%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java => thingmodel/model/dataType/ThingModelArrayDataSpecs.java} (77%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java => thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java} (85%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelDataSpecs.java => thingmodel/model/dataType/ThingModelDataSpecs.java} (51%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java => thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java} (84%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelNumericDataSpec.java => thingmodel/model/dataType/ThingModelNumericDataSpec.java} (91%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/model/dataType/ThinkModelStructDataSpecs.java => thingmodel/model/dataType/ThingModelStructDataSpecs.java} (76%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/vo/IotProductThinkModelPageReqVO.java => thingmodel/vo/IotProductThingModelPageReqVO.java} (76%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/vo/IotProductThinkModelRespVO.java => thingmodel/vo/IotProductThingModelRespVO.java} (77%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{thinkmodel/vo/IotProductThinkModelSaveReqVO.java => thingmodel/vo/IotProductThingModelSaveReqVO.java} (72%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/{ThinkModelMessage.java => ThingModelMessage.java} (97%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/{thinkmodel/IotProductThinkModelDO.java => thingmodel/IotProductThingModelDO.java} (67%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{IotThinkModelMessageService.java => IotThingModelMessageService.java} (66%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/{IotThinkModelMessageServiceImpl.java => IotThingModelMessageServiceImpl.java} (79%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{thinkmodel/IotProductThinkModelService.java => thingmodel/IotProductThingModelService.java} (59%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{thinkmodel/IotProductThinkModelServiceImpl.java => thingmodel/IotProductThingModelServiceImpl.java} (56%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 633b3cb4f2..4539f12591 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -13,14 +13,14 @@ public interface ErrorCodeConstants { ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_050_001_000, "产品不存在"); ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); - ErrorCode PRODUCT_STATUS_NOT_ALLOW_FUNCTION = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); + ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); // ========== 产品物模型 1-050-002-000 ============ - ErrorCode THINK_MODEL_FUNCTION_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); - ErrorCode THINK_MODEL_FUNCTION_EXISTS_BY_PRODUCT_KEY = new ErrorCode(1_050_002_001, "ProductKey 对应的产品物模型已存在"); - ErrorCode THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS = new ErrorCode(1_050_002_002, "存在重复的功能标识符。"); - ErrorCode THINK_MODEL_FUNCTION_NAME_EXISTS = new ErrorCode(1_050_002_003, "存在重复的功能名称。"); - ErrorCode THINK_MODEL_FUNCTION_IDENTIFIER_INVALID = new ErrorCode(1_050_002_003, "产品物模型标识无效"); + ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); + ErrorCode THING_MODEL_EXISTS_BY_PRODUCT_KEY = new ErrorCode(1_050_002_001, "ProductKey 对应的产品物模型已存在"); + ErrorCode THING_MODEL_IDENTIFIER_EXISTS = new ErrorCode(1_050_002_002, "存在重复的功能标识符。"); + ErrorCode THING_MODEL_NAME_EXISTS = new ErrorCode(1_050_002_003, "存在重复的功能名称。"); + ErrorCode THING_MODEL_IDENTIFIER_INVALID = new ErrorCode(1_050_002_003, "产品物模型标识无效"); // ========== 设备 1-050-003-000 ============ ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelAccessModeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java index b7f6ca312d..a755018034 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.enums.thinkmodel; +package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,7 +10,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotProductThinkModelAccessModeEnum { +public enum IotProductThingModelAccessModeEnum { READ_ONLY("r"), READ_WRITE("rw"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java similarity index 71% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java index 0ed00d08f9..153e93ecc8 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thinkmodel/IotProductThinkModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.enums.thinkmodel; +package cn.iocoder.yudao.module.iot.enums.thingmodel; import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; @@ -13,13 +13,13 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum IotProductThinkModelTypeEnum implements IntArrayValuable { +public enum IotProductThingModelTypeEnum implements IntArrayValuable { PROPERTY(1, "属性"), SERVICE(2, "服务"), EVENT(3, "事件"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThinkModelTypeEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThingModelTypeEnum::getType).toArray(); /** * 类型 @@ -30,8 +30,8 @@ public enum IotProductThinkModelTypeEnum implements IntArrayValuable { */ private final String description; - public static IotProductThinkModelTypeEnum valueOfType(Integer type) { - for (IotProductThinkModelTypeEnum value : values()) { + public static IotProductThingModelTypeEnum valueOfType(Integer type) { + for (IotProductThingModelTypeEnum value : values()) { if (value.getType().equals(type)) { return value; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java index ad32fb5f9d..f25377e2f6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataRespVO.java @@ -13,7 +13,7 @@ public class IotDeviceDataRespVO { private Long deviceId; @Schema(description = "物模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") - private Long thinkModelFunctionId; + private Long thingModelId; @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) private String productKey; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThingModelController.http rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http index 2343eed38c..7742705ef1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThingModelController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http @@ -1,5 +1,5 @@ -### 请求 /iot/product-think-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-think-model/create +### 请求 /iot/product-thing-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-thing-model/create Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -30,8 +30,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-think-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-think-model/create +### 请求 /iot/product-thing-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-thing-model/create Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -65,8 +65,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-think-model/create 接口 => 成功 -POST {{baseUrl}}/iot/product-think-model/create +### 请求 /iot/product-thing-model/create 接口 => 成功 +POST {{baseUrl}}/iot/product-thing-model/create Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -134,8 +134,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-think-model/update 接口 => 成功 -PUT {{baseUrl}}/iot/product-think-model/update +### 请求 /iot/product-thing-model/update 接口 => 成功 +PUT {{baseUrl}}/iot/product-thing-model/update Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -170,18 +170,18 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/product-think-model/delete 接口 => 成功 -DELETE {{baseUrl}}/iot/product-think-model/delete?id=36 +### 请求 /iot/product-thing-model/delete 接口 => 成功 +DELETE {{baseUrl}}/iot/product-thing-model/delete?id=36 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} -### 请求 /iot/product-think-model/get 接口 => 成功 -GET {{baseUrl}}/iot/product-think-model/get?id=40 +### 请求 /iot/product-thing-model/get 接口 => 成功 +GET {{baseUrl}}/iot/product-thing-model/get?id=40 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} -### 请求 /iot/product-think-model/list-by-product-id 接口 => 成功 -GET {{baseUrl}}/iot/product-think-model/list-by-product-id?productId=1001 +### 请求 /iot/product-thing-model/list-by-product-id 接口 => 成功 +GET {{baseUrl}}/iot/product-thing-model/list-by-product-id?productId=1001 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java new file mode 100644 index 0000000000..9b94def428 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 产品物模型") +@RestController +@RequestMapping("/iot/product-thing-model") +@Validated +public class IotProductThingModelController { + + @Resource + private IotProductThingModelService thingModelService; + + @PostMapping("/create") + @Operation(summary = "创建产品物模型") + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:create')") + public CommonResult createProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO createReqVO) { + return success(thingModelService.createProductThingModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品物模型") + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:update')") + public CommonResult updateProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO updateReqVO) { + thingModelService.updateProductThingModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:delete')") + public CommonResult deleteProductThingModel(@RequestParam("id") Long id) { + thingModelService.deleteProductThingModel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") + public CommonResult getProductThingModel(@RequestParam("id") Long id) { + IotProductThingModelDO thingModel = thingModelService.getProductThingModel(id); + return success(IotProductThingModelConvert.INSTANCE.convert(thingModel)); + } + + @GetMapping("/list-by-product-id") + @Operation(summary = "获得产品物模型") + @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") + public CommonResult> getProductThingModelListByProductId(@RequestParam("productId") Long productId) { + List list = thingModelService.getProductThingModelListByProductId(productId); + return success(IotProductThingModelConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品物模型分页") + @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") + public CommonResult> getProductThingModelPage(@Valid IotProductThingModelPageReqVO pageReqVO) { + PageResult pageResult = thingModelService.getProductThingModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotProductThingModelRespVO.class)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java similarity index 61% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelEvent.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 7d16aa50ba..718c7b60e8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; import lombok.Data; import java.util.List; @Data -public class ThinkModelEvent { +public class ThingModelEvent { /** * 事件标识符 @@ -26,7 +26,7 @@ public class ThinkModelEvent { * "info"、"alert"、"error" */ private String type; - private List outputData; + private List outputData; private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java similarity index 69% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelProperty.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index a4d2e98921..0c9105a8b6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -1,7 +1,7 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; import lombok.Data; import java.util.List; @@ -14,7 +14,7 @@ import java.util.List; * @author HUIHUI */ @Data -public class ThinkModelProperty { +public class ThingModelProperty { /** * 属性标识符 @@ -30,7 +30,7 @@ public class ThinkModelProperty { private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThinkModelAccessModeEnum} + * 关联枚举 {@link IotProductThingModelAccessModeEnum} */ private String accessMode; /** @@ -47,10 +47,10 @@ public class ThinkModelProperty { /** * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 */ - private ThinkModelDataSpecs dataSpecs; + private ThingModelDataSpecs dataSpecs; /** * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 */ - private List dataSpecsList; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java similarity index 68% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java index 7eb06ce394..44fd24c0ad 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; import lombok.*; @@ -9,7 +9,7 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor @ToString -public class ThinkModelRespVO { +public class ThingModelRespVO { /** * 产品编号 @@ -35,16 +35,16 @@ public class ThinkModelRespVO { /** * 属性列表 */ - private List properties; + private List properties; /** * 服务列表 */ - private List services; + private List services; /** * 事件列表 */ - private List events; + private List events; } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java similarity index 56% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 851df36564..ec4bd34e99 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/ThinkModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; import lombok.Data; import java.util.List; @Data -public class ThinkModelService { +public class ThingModelService { /** * 服务标识符 @@ -26,8 +26,8 @@ public class ThinkModelService { * "sync"、"async" */ private String callType; - private List inputData; - private List outputData; + private List inputData; + private List outputData; private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java similarity index 64% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java index 7b293ca79e..7b3fb097c4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import lombok.Data; @Data -public class ThinkModelArgument { +public class ThingModelArgument { public static final String DIRECTION_INPUT = "input"; public static final String DIRECTION_OUTPUT = "output"; @@ -14,7 +14,7 @@ public class ThinkModelArgument { /** * 物模型中的属性 */ - private ThinkModelProperty property; + private ThingModelProperty property; /** * 用于区分输入或输出参数,"input" 或 "output" */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java similarity index 77% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java index ebc69f640b..b8b1a29753 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArrayDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -6,7 +6,6 @@ import lombok.EqualsAndHashCode; import java.util.List; -// TODO @puhui999:thingmodel 哈 = = 之前我写错单词了 /** * 物模型数据类型为数组的 DataSpec 定义 * @@ -15,7 +14,7 @@ import java.util.List; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThinkModelArrayDataSpecs extends ThinkModelDataSpecs { +public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { /** * 数组中的元素个数。 @@ -29,7 +28,7 @@ public class ThinkModelArrayDataSpecs extends ThinkModelDataSpecs { * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中。 * 此时 struct 取值范围为:int、float、double、text、date、enum、bool */ - private List dataSpecsList; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java index 8a44b584cd..3ab624cabb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelBoolOrEnumDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThinkModelBoolOrEnumDataSpecs extends ThinkModelDataSpecs { +public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs { /** * 枚举项的名称。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java similarity index 51% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java index 2d01747651..78bfd02dd2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; /** - * 抽象类 ThinkModelDataSpecs + * 抽象类 ThingModelDataSpecs * * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类。 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 @@ -15,17 +15,17 @@ import lombok.Data; @Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "dataType", visible = true) @JsonSubTypes({ - @JsonSubTypes.Type(value = ThinkModelNumericDataSpec.class, name = "int"), - @JsonSubTypes.Type(value = ThinkModelNumericDataSpec.class, name = "float"), - @JsonSubTypes.Type(value = ThinkModelNumericDataSpec.class, name = "double"), - @JsonSubTypes.Type(value = ThinkModelDateOrTextDataSpecs.class, name = "text"), - @JsonSubTypes.Type(value = ThinkModelDateOrTextDataSpecs.class, name = "date"), - @JsonSubTypes.Type(value = ThinkModelBoolOrEnumDataSpecs.class, name = "bool"), - @JsonSubTypes.Type(value = ThinkModelBoolOrEnumDataSpecs.class, name = "enum"), - @JsonSubTypes.Type(value = ThinkModelArrayDataSpecs.class, name = "array"), - @JsonSubTypes.Type(value = ThinkModelStructDataSpecs.class, name = "struct") + @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "int"), + @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "float"), + @JsonSubTypes.Type(value = ThingModelNumericDataSpec.class, name = "double"), + @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "text"), + @JsonSubTypes.Type(value = ThingModelDateOrTextDataSpecs.class, name = "date"), + @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "bool"), + @JsonSubTypes.Type(value = ThingModelBoolOrEnumDataSpecs.class, name = "enum"), + @JsonSubTypes.Type(value = ThingModelArrayDataSpecs.class, name = "array"), + @JsonSubTypes.Type(value = ThingModelStructDataSpecs.class, name = "struct") }) -public abstract class ThinkModelDataSpecs { +public abstract class ThingModelDataSpecs { /** * 数据类型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java similarity index 84% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java index 640316b47c..8d5ddff62a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelDateOrTextDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThinkModelDateOrTextDataSpecs extends ThinkModelDataSpecs { +public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { /** * 数据长度,单位为字节。取值不能超过 2048。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelNumericDataSpec.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java similarity index 91% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelNumericDataSpec.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java index 714b57bd6a..b65d606ac9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelNumericDataSpec.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @@ -14,7 +14,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThinkModelNumericDataSpec extends ThinkModelDataSpecs { +public class ThingModelNumericDataSpec extends ThingModelDataSpecs { /** * 最大值,需转为字符串类型。值必须与 dataType 类型一致。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java similarity index 76% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelStructDataSpecs.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java index 8cbb9afe5b..e0f5bb5397 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java @@ -1,6 +1,6 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,7 +15,7 @@ import java.util.List; @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 -public class ThinkModelStructDataSpecs extends ThinkModelDataSpecs { +public class ThingModelStructDataSpecs extends ThingModelDataSpecs { /** * 属性标识符 @@ -31,7 +31,7 @@ public class ThinkModelStructDataSpecs extends ThinkModelDataSpecs { private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThinkModelAccessModeEnum} + * 关联枚举 {@link IotProductThingModelAccessModeEnum} */ private String accessMode; /** @@ -48,11 +48,11 @@ public class ThinkModelStructDataSpecs extends ThinkModelDataSpecs { /** * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 */ - private ThinkModelDataSpecs dataSpecs; + private ThingModelDataSpecs dataSpecs; /** * 数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 */ - private List dataSpecsList; + private List dataSpecsList; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java similarity index 76% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java index afb412228f..a15ae98a1c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -13,7 +13,7 @@ import lombok.ToString; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IotProductThinkModelPageReqVO extends PageParam { +public class IotProductThingModelPageReqVO extends PageParam { @Schema(description = "功能标识") private String identifier; @@ -22,7 +22,7 @@ public class IotProductThinkModelPageReqVO extends PageParam { private String name; @Schema(description = "功能类型", example = "1") - @InEnum(IotProductThinkModelTypeEnum.class) + @InEnum(IotProductThingModelTypeEnum.class) private Integer type; @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java similarity index 77% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java index a79cd26779..8d9c370d99 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated -public class IotProductThinkModelRespVO { +public class IotProductThingModelRespVO { @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") @ExcelProperty("产品ID") @@ -39,13 +39,13 @@ public class IotProductThinkModelRespVO { private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) - private ThinkModelProperty property; + private ThingModelProperty property; @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) - private ThinkModelEvent event; + private ThingModelEvent event; @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) - private ThinkModelService service; + private ThingModelService service; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java similarity index 72% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java index 3cce60427d..5f2be48478 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/vo/IotProductThinkModelSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java @@ -1,10 +1,10 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo; +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -12,7 +12,7 @@ import lombok.Data; @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data -public class IotProductThinkModelSaveReqVO { +public class IotProductThingModelSaveReqVO { @Schema(description = "编号", example = "1") private Long id; @@ -38,16 +38,16 @@ public class IotProductThinkModelSaveReqVO { @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "功能类型不能为空") - @InEnum(IotProductThinkModelTypeEnum.class) + @InEnum(IotProductThingModelTypeEnum.class) private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) - private ThinkModelProperty property; + private ThingModelProperty property; @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) - private ThinkModelService service; + private ThingModelService service; @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) - private ThinkModelEvent event; + private ThingModelEvent event; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java deleted file mode 100644 index 03408b59db..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/IotProductThinkModelController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thinkmodel.IotProductThinkModelConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; -import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 产品物模型") -@RestController -@RequestMapping("/iot/product-think-model") -@Validated -public class IotProductThinkModelController { - - @Resource - private IotProductThinkModelService thinkModelFunctionService; - - @PostMapping("/create") - @Operation(summary = "创建产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-think-model:create')") - public CommonResult createProductThinkModel(@Valid @RequestBody IotProductThinkModelSaveReqVO createReqVO) { - return success(thinkModelFunctionService.createProductThinkModel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-think-model:update')") - public CommonResult updateProductThinkModel(@Valid @RequestBody IotProductThinkModelSaveReqVO updateReqVO) { - thinkModelFunctionService.updateProductThinkModel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-think-model:delete')") - public CommonResult deleteProductThinkModel(@RequestParam("id") Long id) { - thinkModelFunctionService.deleteProductThinkModel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-think-model:query')") - public CommonResult getProductThinkModel(@RequestParam("id") Long id) { - IotProductThinkModelDO function = thinkModelFunctionService.getProductThinkModel(id); - return success(IotProductThinkModelConvert.INSTANCE.convert(function)); - } - - @GetMapping("/list-by-product-id") - @Operation(summary = "获得产品物模型") - @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:product-think-model:query')") - public CommonResult> getProductThinkModelListByProductId(@RequestParam("productId") Long productId) { - List list = thinkModelFunctionService.getProductThinkModelListByProductId(productId); - return success(IotProductThinkModelConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @Operation(summary = "获得产品物模型分页") - @PreAuthorize("@ss.hasPermission('iot:product-think-model:query')") - public CommonResult> getProductThinkModelPage(@Valid IotProductThinkModelPageReqVO pageReqVO) { - PageResult pageResult = thinkModelFunctionService.getProductThinkModelPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotProductThinkModelRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java index 22d5dda6dc..d8f6e47042 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java @@ -12,7 +12,7 @@ public interface IotDeviceDataConvert { // default List convert(Map deviceData, IotDeviceDO device){ // List list = new ArrayList<>(); // deviceData.forEach((identifier, value) -> { -//// ThinkModelProperty property = ThinkModelService.INSTANCE.getProperty(device.getProductId(), identifier); +//// ThingModelProperty property = ThingModelService.INSTANCE.getProperty(device.getProductId(), identifier); //// if (Objects.isNull(property)) { //// return; //// } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java new file mode 100644 index 0000000000..cb10c62b5d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.convert.thingmodel; + +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Objects; + +@Mapper +public interface IotProductThingModelConvert { + + IotProductThingModelConvert INSTANCE = Mappers.getMapper(IotProductThingModelConvert.class); + + // 将 SaveReqVO 转换为 DO + @Mapping(target = "property", expression = "java(convertToProperty(bean))") + @Mapping(target = "event", expression = "java(convertToEvent(bean))") + @Mapping(target = "service", expression = "java(convertToService(bean))") + IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); + + // 将 DO 转换为 RespVO + @Mapping(target = "property", source = "property") + @Mapping(target = "event", source = "event") + @Mapping(target = "service", source = "service") + IotProductThingModelRespVO convert(IotProductThingModelDO bean); + + // 批量转换 + List convertList(List list); + + @Named("convertToProperty") + default ThingModelProperty convertToProperty(IotProductThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + return bean.getProperty(); + } + return null; + } + + @Named("convertToEvent") + default ThingModelEvent convertToEvent(IotProductThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { + return bean.getEvent(); + } + return null; + } + + @Named("convertToService") + default ThingModelService convertToService(IotProductThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { + return bean.getService(); + } + return null; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java deleted file mode 100644 index bb5c22b356..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thinkmodel/IotProductThinkModelConvert.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.convert.thinkmodel; - -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; - -import java.util.List; -import java.util.Objects; - -@Mapper -public interface IotProductThinkModelConvert { - - IotProductThinkModelConvert INSTANCE = Mappers.getMapper(IotProductThinkModelConvert.class); - - // 将 SaveReqVO 转换为 DO - @Mapping(target = "property", expression = "java(convertToProperty(bean))") - @Mapping(target = "event", expression = "java(convertToEvent(bean))") - @Mapping(target = "service", expression = "java(convertToService(bean))") - IotProductThinkModelDO convert(IotProductThinkModelSaveReqVO bean); - - // 将 DO 转换为 RespVO - @Mapping(target = "property", source = "property") - @Mapping(target = "event", source = "event") - @Mapping(target = "service", source = "service") - IotProductThinkModelRespVO convert(IotProductThinkModelDO bean); - - // 批量转换 - List convertList(List list); - - @Named("convertToProperty") - default ThinkModelProperty convertToProperty(IotProductThinkModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { - return bean.getProperty(); - } - return null; - } - - @Named("convertToEvent") - default ThinkModelEvent convertToEvent(IotProductThinkModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThinkModelTypeEnum.EVENT.getType())) { - return bean.getEvent(); - } - return null; - } - - @Named("convertToService") - default ThinkModelService convertToService(IotProductThinkModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThinkModelTypeEnum.SERVICE.getType())) { - return bean.getService(); - } - return null; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index c3ed4863ee..ab1934edbf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -30,9 +30,9 @@ public class IotDeviceDataDO { /** * 物模型编号 *

- * 关联 {@link IotProductThinkModelDO#getId()} + * 关联 {@link IotProductThingModelDO#getId()} */ - private Long thinkModelId; + private Long thingModelId; /** * 产品标识 @@ -51,21 +51,21 @@ public class IotDeviceDataDO { /** * 属性标识符 *

- * 关联 {@link IotProductThinkModelDO#getIdentifier()} + * 关联 {@link IotProductThingModelDO#getIdentifier()} */ private String identifier; /** * 属性名称 *

- * 关联 {@link IotProductThinkModelDO#getName()} + * 关联 {@link IotProductThingModelDO#getName()} */ private String name; /** * 数据类型 *

- * 关联 {@link IotProductThinkModelDO#getProperty()#getDataType()} + * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} */ private String dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 81bfed068f..637b2228be 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; import java.util.HashMap; import java.util.List; @@ -33,7 +33,7 @@ public class FieldParser { * @param property 物模型属性 * @return TdField对象 */ - public static TdFieldDO parse(ThinkModelProperty property) { + public static TdFieldDO parse(ThingModelProperty property) { String fieldName = property.getIdentifier().toLowerCase(); // 将物模型字段类型映射为td字段类型 @@ -53,7 +53,7 @@ public class FieldParser { * @param thingModel 物模型响应对象 * @return 字段列表 */ - public static List parse(ThinkModelRespVO thingModel) { + public static List parse(ThingModelRespVO thingModel) { return thingModel.getModel().getProperties().stream() .map(FieldParser::parse) .collect(Collectors.toList()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java similarity index 97% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessage.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java index 22cf9e2ef6..d5009dc244 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessage.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java @@ -15,7 +15,7 @@ import java.util.Map; @NoArgsConstructor @AllArgsConstructor @Builder -public class ThinkModelMessage { +public class ThingModelMessage { /** * 消息ID diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodel/IotProductThinkModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java similarity index 67% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodel/IotProductThinkModelDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java index 76b2a63e73..3d9e5e5fe0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thinkmodel/IotProductThinkModelDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel; +package cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -19,17 +19,17 @@ import lombok.NoArgsConstructor; /** * IoT 产品物模型功能 DO *

- * 每个 {@link IotProductDO} 和 {@link IotProductThinkModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 + * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 * * @author 芋道源码 */ -@TableName(value = "iot_product_think_model", autoResultMap = true) -@KeySequence("iot_product_think_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName(value = "iot_product_thing_model", autoResultMap = true) +@KeySequence("iot_product_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class IotProductThinkModelDO extends BaseDO { +public class IotProductThingModelDO extends BaseDO { /** * 物模型功能编号 @@ -66,7 +66,7 @@ public class IotProductThinkModelDO extends BaseDO { /** * 功能类型 *

- * 枚举 {@link IotProductThinkModelTypeEnum} + * 枚举 {@link IotProductThingModelTypeEnum} */ private Integer type; @@ -74,18 +74,18 @@ public class IotProductThinkModelDO extends BaseDO { * 属性 */ @TableField(typeHandler = JacksonTypeHandler.class) - private ThinkModelProperty property; + private ThingModelProperty property; /** * 事件 */ @TableField(typeHandler = JacksonTypeHandler.class) - private ThinkModelEvent event; + private ThingModelEvent event; /** * 服务 */ @TableField(typeHandler = JacksonTypeHandler.class) - private ThinkModelService service; + private ThingModelService service; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java new file mode 100644 index 0000000000..175f66f402 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IoT 产品物模型 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotProductThingModelMapper extends BaseMapperX { + + default PageResult selectPage(IotProductThingModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) + .eqIfPresent(IotProductThingModelDO::getType, reqVO.getType()) + .eqIfPresent(IotProductThingModelDO::getProductId, reqVO.getProductId()) + .notIn(IotProductThingModelDO::getIdentifier, "get", "set", "post") + .orderByDesc(IotProductThingModelDO::getId)); + } + + default IotProductThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { + return selectOne(IotProductThingModelDO::getProductId, productId, + IotProductThingModelDO::getIdentifier, identifier); + } + + default List selectListByProductId(Long productId) { + return selectList(IotProductThingModelDO::getProductId, productId); + } + + default List selectListByProductIdAndType(Long productId, Integer type) { + return selectList(IotProductThingModelDO::getProductId, productId, + IotProductThingModelDO::getType, type); + } + + default List selectListByProductIdAndIdentifiersAndTypes(Long productId, + List identifiers, + List types) { + return selectList(new LambdaQueryWrapperX() + .eq(IotProductThingModelDO::getProductId, productId) + .in(IotProductThingModelDO::getIdentifier, identifiers) + .in(IotProductThingModelDO::getType, types)); + } + + default IotProductThingModelDO selectByProductIdAndName(Long productId, String name) { + return selectOne(IotProductThingModelDO::getProductId, productId, + IotProductThingModelDO::getName, name); + } + + default List selectListByProductKey(String productKey) { + return selectList(IotProductThingModelDO::getProductKey, productKey); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java deleted file mode 100644 index de76aa6336..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thinkmodel/IotProductThinkModelMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.thinkmodel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 产品物模型 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotProductThinkModelMapper extends BaseMapperX { - - default PageResult selectPage(IotProductThinkModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotProductThinkModelDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotProductThinkModelDO::getName, reqVO.getName()) - .eqIfPresent(IotProductThinkModelDO::getType, reqVO.getType()) - .eqIfPresent(IotProductThinkModelDO::getProductId, reqVO.getProductId()) - .notIn(IotProductThinkModelDO::getIdentifier, "get", "set", "post") - .orderByDesc(IotProductThinkModelDO::getId)); - } - - default IotProductThinkModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { - return selectOne(IotProductThinkModelDO::getProductId, productId, - IotProductThinkModelDO::getIdentifier, identifier); - } - - default List selectListByProductId(Long productId) { - return selectList(IotProductThinkModelDO::getProductId, productId); - } - - default List selectListByProductIdAndType(Long productId, Integer type) { - return selectList(IotProductThinkModelDO::getProductId, productId, - IotProductThinkModelDO::getType, type); - } - - default List selectListByProductIdAndIdentifiersAndTypes(Long productId, - List identifiers, - List types) { - return selectList(new LambdaQueryWrapperX() - .eq(IotProductThinkModelDO::getProductId, productId) - .in(IotProductThinkModelDO::getIdentifier, identifiers) - .in(IotProductThinkModelDO::getType, types)); - } - - default IotProductThinkModelDO selectByProductIdAndName(Long productId, String name) { - return selectOne(IotProductThinkModelDO::getProductId, productId, - IotProductThinkModelDO::getName, name); - } - - default List selectListByProductKey(String productKey) { - return selectList(IotProductThinkModelDO::getProductKey, productKey); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java index 71780824a0..4cb39c0bc5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java @@ -8,14 +8,14 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.tdengine.IotThinkModelMessageService; -import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; @@ -40,9 +40,9 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { @Resource private IotDeviceService deviceService; @Resource - private IotThinkModelMessageService thingModelMessageService; + private IotThingModelMessageService thingModelMessageService; @Resource - private IotProductThinkModelService thinkModelFunctionService; + private IotProductThingModelService thingModelService; @Resource private TdEngineDMLMapper tdEngineDMLMapper; @@ -56,7 +56,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 2. 解析消息,保存数据 JSONObject jsonObject = new JSONObject(message); log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject); - ThinkModelMessage thingModelMessage = ThinkModelMessage.builder() + ThingModelMessage thingModelMessage = ThingModelMessage.builder() .id(jsonObject.getStr("id")) .sys(jsonObject.get("sys")) .method(jsonObject.getStr("method")) @@ -66,7 +66,7 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { .deviceName(deviceName) .deviceKey(device.getDeviceKey()) .build(); - thingModelMessageService.saveThinkModelMessage(device, thingModelMessage); + thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } @Override @@ -75,31 +75,31 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thinkModelList = thinkModelFunctionService.getProductThinkModelListByProductKey(device.getProductKey()); - thinkModelList = filterList(thinkModelList, thinkModel -> IotProductThinkModelTypeEnum.PROPERTY.getType() - .equals(thinkModel.getType())); + List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); + thingModelList = filterList(thingModelList, thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType() + .equals(thingModel.getType())); // 3. 过滤标识符和属性名称 if (deviceDataReqVO.getIdentifier() != null) { - thinkModelList = filterList(thinkModelList, thinkModel -> thinkModel.getIdentifier() + thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier() .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())); } if (deviceDataReqVO.getName() != null) { - thinkModelList = filterList(thinkModelList, thinkModel -> thinkModel.getName() + thingModelList = filterList(thingModelList, thingModel -> thingModel.getName() .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())); } // 4. 获取设备属性最新数据 - thinkModelList.forEach(thinkModel -> { - IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thinkModel.getIdentifier()); + thingModelList.forEach(thingModel -> { + IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier()); if (deviceData == null) { deviceData = new IotDeviceDataDO(); deviceData.setProductKey(device.getProductKey()); deviceData.setDeviceName(device.getDeviceName()); - deviceData.setIdentifier(thinkModel.getIdentifier()); + deviceData.setIdentifier(thingModel.getIdentifier()); deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); - deviceData.setThinkModelId(thinkModel.getId()); - deviceData.setName(thinkModel.getName()); - deviceData.setDataType(thinkModel.getProperty().getDataType()); + deviceData.setThingModelId(thingModel.getId()); + deviceData.setName(thingModel.getName()); + deviceData.setDataType(thingModel.getProperty().getDataType()); } list.add(deviceData); }); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index b26c2c123b..4fd6cfff0a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -34,7 +34,7 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy - private IotProductThinkModelService thinkModelFunctionService; + private IotProductThingModelService thingModelService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { @@ -115,7 +115,7 @@ public class IotProductServiceImpl implements IotProductService { // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建超级表数据模型 - thinkModelFunctionService.createSuperTableDataModel(id); + thingModelService.createSuperTableDataModel(id); } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java index bf76ae1c32..bb6d8c0772 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import java.util.List; @@ -14,5 +14,6 @@ public interface IotSuperTableService { /** * 创建超级表数据模型 */ - void createSuperTableDataModel(IotProductDO product, List functionList); + void createSuperTableDataModel(IotProductDO product, List thingModelList); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java index 59fa9d0f30..1727fae624 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java @@ -2,15 +2,15 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -33,8 +33,8 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { private String url; @Override - public void createSuperTableDataModel(IotProductDO product, List functionList) { - ThinkModelRespVO thingModel = buildThinkModel(product, functionList); + public void createSuperTableDataModel(IotProductDO product, List thingModelList) { + ThingModelRespVO thingModel = buildThingModel(product, thingModelList); if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { log.warn("物模型属性列表为空,不创建超级表"); @@ -56,7 +56,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 创建超级表 */ - private void createSuperTable(ThinkModelRespVO thingModel, Integer deviceType) { + private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { // 解析物模型,获取字段列表 List schemaFields = new ArrayList<>(); schemaFields.add(TdFieldDO.builder() @@ -84,7 +84,7 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 更新超级表 */ - private void updateSuperTable(ThinkModelRespVO thingModel, Integer deviceType) { + private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); try { List oldFields = getTableFields(superTableName); @@ -210,18 +210,18 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 构建物模型 */ - private ThinkModelRespVO buildThinkModel(IotProductDO product, List functionList) { - ThinkModelRespVO thingModel = new ThinkModelRespVO(); + private ThingModelRespVO buildThingModel(IotProductDO product, List thingModelList) { + ThingModelRespVO thingModel = new ThingModelRespVO(); thingModel.setId(product.getId()); thingModel.setProductKey(product.getProductKey()); - List properties = functionList.stream() - .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.equals( - IotProductThinkModelTypeEnum.valueOfType(function.getType()))) - .map(this::buildThinkModelProperty) + List properties = thingModelList.stream() + .filter(item -> IotProductThingModelTypeEnum.PROPERTY.equals( + IotProductThingModelTypeEnum.valueOfType(item.getType()))) + .map(this::buildThingModelProperty) .collect(Collectors.toList()); - ThinkModelRespVO.Model model = new ThinkModelRespVO.Model(); + ThingModelRespVO.Model model = new ThingModelRespVO.Model(); model.setProperties(properties); thingModel.setModel(model); @@ -231,9 +231,9 @@ public class IotSuperTableServiceImpl implements IotSuperTableService { /** * 构建物模型属性 */ - private ThinkModelProperty buildThinkModelProperty(IotProductThinkModelDO function) { - ThinkModelProperty property = BeanUtil.copyProperties(function, ThinkModelProperty.class); - property.setDataType(function.getProperty().getDataType()); + private ThingModelProperty buildThingModelProperty(IotProductThingModelDO thingModel) { + ThingModelProperty property = BeanUtil.copyProperties(thingModel, ThingModelProperty.class); + property.setDataType(thingModel.getProperty().getDataType()); return property; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java similarity index 66% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java index 67ee2b99ae..ffcb3063c5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; /** * 物模型消息 Service */ -public interface IotThinkModelMessageService { +public interface IotThingModelMessageService { /** * 保存物模型消息 @@ -14,5 +14,5 @@ public interface IotThinkModelMessageService { * @param device 设备 * @param thingModelMessage 物模型消息 */ - void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage); + void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java similarity index 79% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 3c2debfa49..f5d589287f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -10,16 +10,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -28,12 +28,14 @@ import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; + /** * 物模型消息 Service 实现类 */ @Slf4j @Service -public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageService { +public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { private static final String TAG_NOTE = "TAG"; private static final String NOTE = "note"; @@ -47,7 +49,7 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ private String url; @Resource - private IotProductThinkModelService iotProductThinkModelService; + private IotProductThingModelService iotProductThingModelService; @Resource private IotDeviceService iotDeviceService; @Resource @@ -61,7 +63,7 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @TenantIgnore - public void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage) { + public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); @@ -71,13 +73,13 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 Map params = thingModelMessage.dataToMap(); - List functionList = getValidFunctionList(thingModelMessage.getProductKey()); - if (functionList.isEmpty()) { + List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); + if (thingModelList.isEmpty()) { return; } // 3. 过滤并收集有效的属性字段,缓存设备属性 - List schemaFieldValues = filterAndCollectValidFields(params, functionList, device, thingModelMessage.getTime()); + List schemaFieldValues = filterAndCollectValidFields(params, thingModelList, device, thingModelMessage.getTime()); if (schemaFieldValues.size() == 1) { // 仅有时间字段,无需保存 return; } @@ -90,21 +92,17 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ .build()); } - private List getValidFunctionList(String productKey) { - return iotProductThinkModelService - .getProductThinkModelListByProductKey(productKey) - .stream() - .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType().equals(function.getType())) - .toList(); + private List getValidThingModelList(String productKey) { + return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), + thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } - private List filterAndCollectValidFields(Map params, List functionList, IotDeviceDO device, Long time) { + private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { // 1. 获取属性标识符集合 - Set propertyIdentifiers = CollectionUtils.convertSet(functionList, IotProductThinkModelDO::getIdentifier); + Set propertyIdentifiers = convertSet(thingModelList, IotProductThingModelDO::getIdentifier); // 2. 构建属性标识符和属性的映射 - Map functionMap = functionList.stream() - .collect(Collectors.toMap(IotProductThinkModelDO::getIdentifier, function -> function)); + Map thingModelMap = convertMap(thingModelList, IotProductThingModelDO::getIdentifier); // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); @@ -114,7 +112,7 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); // 缓存设备属性 // TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读 - setDeviceDataCache(device, functionMap.get(key), val, time); + setDeviceDataCache(device, thingModelMap.get(key), val, time); } }); return schemaFieldValues; @@ -123,22 +121,22 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ /** * 缓存设备属性 * - * @param device 设备信息 - * @param iotProductThinkModelDO 物模型属性 - * @param val 属性值 - * @param time 时间 + * @param device 设备信息 + * @param iotProductThingModelDO 物模型属性 + * @param val 属性值 + * @param time 时间 */ - private void setDeviceDataCache(IotDeviceDO device, IotProductThinkModelDO iotProductThinkModelDO, Object val, Long time) { + private void setDeviceDataCache(IotDeviceDO device, IotProductThingModelDO iotProductThingModelDO, Object val, Long time) { IotDeviceDataDO deviceData = IotDeviceDataDO.builder() .productKey(device.getProductKey()) .deviceName(device.getDeviceName()) - .identifier(iotProductThinkModelDO.getIdentifier()) + .identifier(iotProductThingModelDO.getIdentifier()) .value(val != null ? val.toString() : null) .updateTime(DateUtil.toLocalDateTime(new Date(time))) .deviceId(device.getId()) - .thinkModelId(iotProductThinkModelDO.getId()) - .name(iotProductThinkModelDO.getName()) - .dataType(iotProductThinkModelDO.getProperty().getDataType()) + .thingModelId(iotProductThingModelDO.getId()) + .name(iotProductThingModelDO.getName()) + .dataType(iotProductThingModelDO.getProperty().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java similarity index 59% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java index 34e7fc1498..04086d0a76 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java @@ -1,9 +1,9 @@ -package cn.iocoder.yudao.module.iot.service.thinkmodel; +package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import jakarta.validation.Valid; import java.util.List; @@ -13,7 +13,7 @@ import java.util.List; * * @author 芋道源码 */ -public interface IotProductThinkModelService { +public interface IotProductThingModelService { /** * 创建产品物模型 @@ -21,21 +21,21 @@ public interface IotProductThinkModelService { * @param createReqVO 创建信息 * @return 编号 */ - Long createProductThinkModel(@Valid IotProductThinkModelSaveReqVO createReqVO); + Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); /** * 更新产品物模型 * * @param updateReqVO 更新信息 */ - void updateProductThinkModel(@Valid IotProductThinkModelSaveReqVO updateReqVO); + void updateProductThingModel(@Valid IotProductThingModelSaveReqVO updateReqVO); /** * 删除产品物模型 * * @param id 编号 */ - void deleteProductThinkModel(Long id); + void deleteProductThingModel(Long id); /** * 获得产品物模型 @@ -43,7 +43,7 @@ public interface IotProductThinkModelService { * @param id 编号 * @return 产品物模型 */ - IotProductThinkModelDO getProductThinkModel(Long id); + IotProductThingModelDO getProductThingModel(Long id); /** * 获得产品物模型列表 @@ -51,7 +51,7 @@ public interface IotProductThinkModelService { * @param productId 产品编号 * @return 产品物模型列表 */ - List getProductThinkModelListByProductId(Long productId); + List getProductThingModelListByProductId(Long productId); /** * 获得产品物模型分页 @@ -59,7 +59,7 @@ public interface IotProductThinkModelService { * @param pageReqVO 分页查询 * @return 产品物模型分页 */ - PageResult getProductThinkModelPage(IotProductThinkModelPageReqVO pageReqVO); + PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); /** * 创建超级表数据模型 @@ -74,6 +74,6 @@ public interface IotProductThinkModelService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getProductThinkModelListByProductKey(String productKey); + List getProductThingModelListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java similarity index 56% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index fa15083f02..b5c2d4eee0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodel/IotProductThinkModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -1,25 +1,25 @@ -package cn.iocoder.yudao.module.iot.service.thinkmodel; +package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArgument; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelArrayDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType.ThinkModelDateOrTextDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.vo.IotProductThinkModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thinkmodel.IotProductThinkModelConvert; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArrayDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodel.IotProductThinkModelMapper; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelAccessModeEnum; -import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; @@ -43,10 +43,10 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j -public class IotProductThinkModelServiceImpl implements IotProductThinkModelService { +public class IotProductThingModelServiceImpl implements IotProductThingModelService { @Resource - private IotProductThinkModelMapper productThinkModelMapper; + private IotProductThingModelMapper productThingModelMapper; @Resource private IotProductService productService; @@ -55,7 +55,7 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ @Override @Transactional(rollbackFor = Exception.class) - public Long createProductThinkModel(IotProductThinkModelSaveReqVO createReqVO) { + public Long createProductThingModel(IotProductThingModelSaveReqVO createReqVO) { // 1. 校验功能标识符在同一产品下是否唯一 validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); @@ -69,52 +69,49 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ validateProductStatus(createReqVO.getProductId()); // 5. 插入数据库 - IotProductThinkModelDO function = IotProductThinkModelConvert.INSTANCE.convert(createReqVO); - productThinkModelMapper.insert(function); + IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(createReqVO); + productThingModelMapper.insert(thingModel); // 6. 如果创建的是属性,需要更新默认的事件和服务 - if (Objects.equals(createReqVO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } - return function.getId(); + return thingModel.getId(); } private void validateProductStatus(Long createReqVO) { IotProductDO product = productService.getProduct(createReqVO); if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { - throw exception(PRODUCT_STATUS_NOT_ALLOW_FUNCTION); + throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); } } private void validateNotDefaultEventAndService(String identifier) { // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { - throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_INVALID); + throw exception(THING_MODEL_IDENTIFIER_INVALID); } -// if (CollUtil.containsAny(Arrays.asList("post", "set", "get"), identifier)) { -// throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_INVALID); -// } } private void validateNameUnique(Long productId, String name) { - IotProductThinkModelDO function = productThinkModelMapper.selectByProductIdAndName(productId, name); - if (function != null) { - throw exception(THINK_MODEL_FUNCTION_NAME_EXISTS); + IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndName(productId, name); + if (thingModel != null) { + throw exception(THING_MODEL_NAME_EXISTS); } } private void validateIdentifierUnique(Long productId, String identifier) { - IotProductThinkModelDO function = productThinkModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (function != null) { - throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); + IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + if (thingModel != null) { + throw exception(THING_MODEL_IDENTIFIER_EXISTS); } } @Override @Transactional(rollbackFor = Exception.class) - public void updateProductThinkModel(IotProductThinkModelSaveReqVO updateReqVO) { + public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { // 1. 校验功能是否存在 - validateProductThinkModelMapperExists(updateReqVO.getId()); + validateProductThingModelMapperExists(updateReqVO.getId()); // 2. 校验功能标识符是否唯一 validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); @@ -123,40 +120,40 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ validateProductStatus(updateReqVO.getProductId()); // 4. 更新数据库 - IotProductThinkModelDO productThinkModelDO = IotProductThinkModelConvert.INSTANCE.convert(updateReqVO); - productThinkModelMapper.updateById(productThinkModelDO); + IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); + productThingModelMapper.updateById(thingModel); // 5. 如果更新的是属性,需要更新默认的事件和服务 - if (Objects.equals(updateReqVO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } } private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotProductThinkModelDO function = productThinkModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (function != null && ObjectUtil.notEqual(function.getId(), id)) { - throw exception(THINK_MODEL_FUNCTION_IDENTIFIER_EXISTS); + IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { + throw exception(THING_MODEL_IDENTIFIER_EXISTS); } } @Override @Transactional(rollbackFor = Exception.class) - public void deleteProductThinkModel(Long id) { + public void deleteProductThingModel(Long id) { // 1. 校验功能是否存在 - IotProductThinkModelDO functionDO = productThinkModelMapper.selectById(id); - if (functionDO == null) { - throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); + IotProductThingModelDO thingModel = productThingModelMapper.selectById(id); + if (thingModel == null) { + throw exception(THING_MODEL_NOT_EXISTS); } // 3. 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(functionDO.getProductId()); + validateProductStatus(thingModel.getProductId()); // 2. 删除功能 - productThinkModelMapper.deleteById(id); + productThingModelMapper.deleteById(id); // 3. 如果删除的是属性,需要更新默认的事件和服务 - if (Objects.equals(functionDO.getType(), IotProductThinkModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(functionDO.getProductId(), functionDO.getProductKey()); + if (Objects.equals(thingModel.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); } } @@ -165,25 +162,25 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ * * @param id 功能编号 */ - private void validateProductThinkModelMapperExists(Long id) { - if (productThinkModelMapper.selectById(id) == null) { - throw exception(THINK_MODEL_FUNCTION_NOT_EXISTS); + private void validateProductThingModelMapperExists(Long id) { + if (productThingModelMapper.selectById(id) == null) { + throw exception(THING_MODEL_NOT_EXISTS); } } @Override - public IotProductThinkModelDO getProductThinkModel(Long id) { - return productThinkModelMapper.selectById(id); + public IotProductThingModelDO getProductThingModel(Long id) { + return productThingModelMapper.selectById(id); } @Override - public List getProductThinkModelListByProductId(Long productId) { - return productThinkModelMapper.selectListByProductId(productId); + public List getProductThingModelListByProductId(Long productId) { + return productThingModelMapper.selectListByProductId(productId); } @Override - public PageResult getProductThinkModelPage(IotProductThinkModelPageReqVO pageReqVO) { - return productThinkModelMapper.selectPage(pageReqVO); + public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { + return productThingModelMapper.selectPage(pageReqVO); } @Override @@ -192,15 +189,15 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ IotProductDO product = productService.getProduct(productId); // 2. 查询产品的物模型功能列表 - List functionList = productThinkModelMapper.selectListByProductId(productId); + List thingModelList = productThingModelMapper.selectListByProductId(productId); // 3. 生成 TDengine 的数据模型 - dbStructureDataService.createSuperTableDataModel(product, functionList); + dbStructureDataService.createSuperTableDataModel(product, thingModelList); } @Override - public List getProductThinkModelListByProductKey(String productKey) { - return productThinkModelMapper.selectListByProductKey(productKey); + public List getProductThingModelListByProductKey(String productKey) { + return productThingModelMapper.selectListByProductKey(productKey); } /** @@ -208,124 +205,124 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 - List propertyList = productThinkModelMapper - .selectListByProductIdAndType(productId, IotProductThinkModelTypeEnum.PROPERTY.getType()); + List propertyList = productThingModelMapper + .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 - List newFunctionList = new ArrayList<>(); + List newThingModelList = new ArrayList<>(); // 生成属性上报事件 - ThinkModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); + ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); if (propertyPostEvent != null) { - IotProductThinkModelDO eventFunction = buildEventFunctionDO(productId, productKey, propertyPostEvent); - newFunctionList.add(eventFunction); + IotProductThingModelDO eventThingModel = buildEventThingModelDO(productId, productKey, propertyPostEvent); + newThingModelList.add(eventThingModel); } // 生成属性设置服务 - ThinkModelService propertySetService = generatePropertySetService(propertyList); + ThingModelService propertySetService = generatePropertySetService(propertyList); if (propertySetService != null) { - IotProductThinkModelDO setServiceFunction = buildServiceFunctionDO(productId, productKey, propertySetService); - newFunctionList.add(setServiceFunction); + IotProductThingModelDO setServiceThingModel = buildServiceThingModelDO(productId, productKey, propertySetService); + newThingModelList.add(setServiceThingModel); } // 生成属性获取服务 - ThinkModelService propertyGetService = generatePropertyGetService(propertyList); + ThingModelService propertyGetService = generatePropertyGetService(propertyList); if (propertyGetService != null) { - IotProductThinkModelDO getServiceFunction = buildServiceFunctionDO(productId, productKey, propertyGetService); - newFunctionList.add(getServiceFunction); + IotProductThingModelDO getServiceThingModel = buildServiceThingModelDO(productId, productKey, propertyGetService); + newThingModelList.add(getServiceThingModel); } // 3. 获取数据库中的默认的旧事件和服务列表 - List oldFunctionList = productThinkModelMapper.selectListByProductIdAndIdentifiersAndTypes( + List oldThingModelList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), - Arrays.asList(IotProductThinkModelTypeEnum.EVENT.getType(), IotProductThinkModelTypeEnum.SERVICE.getType()) + Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) ); // 3.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldFunctionList, newFunctionList, + List> diffResult = diffList(oldThingModelList, newThingModelList, // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier()) && Objects.equals(oldFunc.getType(), newFunc.getType())); - List createList = diffResult.get(0); // 需要新增的 - List updateList = diffResult.get(1); // 需要更新的 - List deleteList = diffResult.get(2); // 需要删除的 + List createList = diffResult.get(0); // 需要新增的 + List updateList = diffResult.get(1); // 需要更新的 + List deleteList = diffResult.get(2); // 需要删除的 // 3.2 批量执行数据库操作 // 新增数据库中的新事件和服务列表 if (CollUtil.isNotEmpty(createList)) { - productThinkModelMapper.insertBatch(createList); + productThingModelMapper.insertBatch(createList); } // 更新数据库中的事件和服务列表 if (CollUtil.isNotEmpty(updateList)) { // 首先,为每个需要更新的对象设置其对应的 ID updateList.forEach(updateFunc -> { - IotProductThinkModelDO oldFunc = findFunctionByIdentifierAndType( - oldFunctionList, updateFunc.getIdentifier(), updateFunc.getType()); + IotProductThingModelDO oldFunc = findThingModelByIdentifierAndType( + oldThingModelList, updateFunc.getIdentifier(), updateFunc.getType()); if (oldFunc != null) { updateFunc.setId(oldFunc.getId()); } }); // 过滤掉没有设置 ID 的对象 - List validUpdateList = updateList.stream() + List validUpdateList = updateList.stream() .filter(func -> func.getId() != null) .collect(Collectors.toList()); // 执行批量更新 if (CollUtil.isNotEmpty(validUpdateList)) { - productThinkModelMapper.updateBatch(validUpdateList); + productThingModelMapper.updateBatch(validUpdateList); } } // 删除数据库中的旧事件和服务列表 if (CollUtil.isNotEmpty(deleteList)) { - Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThinkModelDO::getId); - productThinkModelMapper.deleteByIds(idsToDelete); + Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); + productThingModelMapper.deleteByIds(idsToDelete); } } /** * 根据标识符和类型查找功能对象 */ - private IotProductThinkModelDO findFunctionByIdentifierAndType(List functionList, - String identifier, Integer type) { - return CollUtil.findOne(functionList, func -> + private IotProductThingModelDO findThingModelByIdentifierAndType(List thingModelList, + String identifier, Integer type) { + return CollUtil.findOne(thingModelList, func -> Objects.equals(func.getIdentifier(), identifier) && Objects.equals(func.getType(), type)); } /** * 构建事件功能对象 */ - private IotProductThinkModelDO buildEventFunctionDO(Long productId, String productKey, ThinkModelEvent event) { - return new IotProductThinkModelDO() + private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event) { + return new IotProductThingModelDO() .setProductId(productId) .setProductKey(productKey) .setIdentifier(event.getIdentifier()) .setName(event.getName()) .setDescription(event.getDescription()) - .setType(IotProductThinkModelTypeEnum.EVENT.getType()) + .setType(IotProductThingModelTypeEnum.EVENT.getType()) .setEvent(event); } /** * 构建服务功能对象 */ - private IotProductThinkModelDO buildServiceFunctionDO(Long productId, String productKey, ThinkModelService service) { - return new IotProductThinkModelDO() + private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service) { + return new IotProductThingModelDO() .setProductId(productId) .setProductKey(productKey) .setIdentifier(service.getIdentifier()) .setName(service.getName()) .setDescription(service.getDescription()) - .setType(IotProductThinkModelTypeEnum.SERVICE.getType()) + .setType(IotProductThingModelTypeEnum.SERVICE.getType()) .setService(service); } /** * 生成属性上报事件 */ - private ThinkModelEvent generatePropertyPostEvent(List propertyList) { + private ThingModelEvent generatePropertyPostEvent(List propertyList) { if (CollUtil.isEmpty(propertyList)) { return null; } - ThinkModelEvent event = new ThinkModelEvent() + ThingModelEvent event = new ThingModelEvent() .setIdentifier("post") .setName("属性上报") .setType("info") @@ -333,13 +330,13 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ .setMethod("thing.event.property.post"); // 将属性列表转换为事件的输出参数 - List outputData = new ArrayList<>(); - for (IotProductThinkModelDO thinkModel : propertyList) { - ThinkModelArgument arg = new ThinkModelArgument() - .setIdentifier(thinkModel.getIdentifier()) - .setName(thinkModel.getName()) - .setProperty(thinkModel.getProperty()) - .setDescription(thinkModel.getDescription()) + List outputData = new ArrayList<>(); + for (IotProductThingModelDO thingModel : propertyList) { + ThingModelArgument arg = new ThingModelArgument() + .setIdentifier(thingModel.getIdentifier()) + .setName(thingModel.getName()) + .setProperty(thingModel.getProperty()) + .setDescription(thingModel.getDescription()) .setDirection("output"); // 设置为输出参数 outputData.add(arg); } @@ -350,16 +347,16 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ /** * 生成属性设置服务 */ - private ThinkModelService generatePropertySetService(List propertyList) { + private ThingModelService generatePropertySetService(List propertyList) { if (propertyList == null || propertyList.isEmpty()) { return null; } - List inputData = new ArrayList<>(); - for (IotProductThinkModelDO thinkModel : propertyList) { - ThinkModelProperty property = thinkModel.getProperty(); - if (IotProductThinkModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { - ThinkModelArgument arg = new ThinkModelArgument() + List inputData = new ArrayList<>(); + for (IotProductThingModelDO thingModel : propertyList) { + ThingModelProperty property = thingModel.getProperty(); + if (IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { + ThingModelArgument arg = new ThingModelArgument() .setIdentifier(property.getIdentifier()) .setName(property.getName()) .setProperty(property) @@ -374,7 +371,7 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ } // 属性设置服务一般不需要输出参数 - return new ThinkModelService() + return new ThingModelService() .setIdentifier("set") .setName("属性设置") .setCallType("async") @@ -388,17 +385,17 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ /** * 生成属性获取服务 */ - private ThinkModelService generatePropertyGetService(List propertyList) { + private ThingModelService generatePropertyGetService(List propertyList) { if (propertyList == null || propertyList.isEmpty()) { return null; } - List outputData = new ArrayList<>(); - for (IotProductThinkModelDO functionDO : propertyList) { - ThinkModelProperty property = functionDO.getProperty(); + List outputData = new ArrayList<>(); + for (IotProductThingModelDO thingModelDO : propertyList) { + ThingModelProperty property = thingModelDO.getProperty(); if (ObjectUtils.equalsAny(property.getAccessMode(), - IotProductThinkModelAccessModeEnum.READ_ONLY.getMode(), IotProductThinkModelAccessModeEnum.READ_WRITE.getMode())) { - ThinkModelArgument arg = new ThinkModelArgument() + IotProductThingModelAccessModeEnum.READ_ONLY.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { + ThingModelArgument arg = new ThingModelArgument() .setIdentifier(property.getIdentifier()) .setName(property.getName()) .setProperty(property) @@ -412,7 +409,7 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ return null; } - ThinkModelService service = new ThinkModelService() + ThingModelService service = new ThingModelService() .setIdentifier("get") .setName("属性获取") .setCallType("async") @@ -420,21 +417,21 @@ public class IotProductThinkModelServiceImpl implements IotProductThinkModelServ .setMethod("thing.service.property.get"); // 定义输入参数:属性标识符列表 - ThinkModelArgument inputArg = new ThinkModelArgument() + ThingModelArgument inputArg = new ThingModelArgument() .setIdentifier("properties") .setName("属性标识符列表") .setDescription("需要获取的属性标识符列表") .setDirection("input"); // 设置为输入参数 // 创建数组类型,元素类型为文本类型(字符串)TODO @puhui999: 还得研究研究 - ThinkModelArrayDataSpecs arrayType = new ThinkModelArrayDataSpecs(); + ThingModelArrayDataSpecs arrayType = new ThingModelArrayDataSpecs(); arrayType.setDataType("array"); - inputArg.setProperty(new ThinkModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) + inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) .setDescription(inputArg.getDescription()).setDataSpecs(arrayType)); - ThinkModelDateOrTextDataSpecs textType = new ThinkModelDateOrTextDataSpecs(); + ThingModelDateOrTextDataSpecs textType = new ThingModelDateOrTextDataSpecs(); textType.setDataType("text"); - inputArg.setProperty(new ThinkModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) + inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) .setDescription(inputArg.getDescription()).setDataSpecs(textType)); service.setInputData(Collections.singletonList(inputArg)); From 067130ecde7211d2063ba0e2a83e03daf7ab9e5e Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Thu, 19 Dec 2024 16:43:56 +0800 Subject: [PATCH 040/309] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E7=89=A9?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E6=97=A5=E5=BF=97=E5=BB=BA=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/enums/IotConstants.java | 7 ++ .../model/dataType/ThinkModelArgument.java | 2 + .../tdengine/ThinkModelMessageDO.java | 66 +++++++++++++++++++ .../tdengine/TdThinkModelMessageMapper.java | 29 ++++++++ .../simulatesend/SimulateSendProducer.java | 20 ++++++ .../product/IotProductServiceImpl.java | 10 ++- .../tdengine/IotThinkModelMessageService.java | 8 +++ .../IotThinkModelMessageServiceImpl.java | 61 +++++++++++++++-- .../module/iot/util/IotTdDatabaseUtils.java | 63 ++++++++++++++++++ .../tdengine/TdThinkModelMessageMapper.xml | 34 ++++++++++ 10 files changed, 292 insertions(+), 8 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java index 5927f44b96..0762ade9de 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java @@ -35,4 +35,11 @@ public interface IotConstants { */ String DEVICE_STABLE_NAME_FORMAT = "device_%s"; + /** + * 获取物模型消息记录设备名 + *

+ * 格式为 thing_model_message_{productKey}_{deviceName} + */ + String THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s"; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java index 7b293ca79e..2ed260fdec 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodel/model/dataType/ThinkModelArgument.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.dataType; import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @Data +@JsonIgnoreProperties(ignoreUnknown = true) public class ThinkModelArgument { public static final String DIRECTION_INPUT = "input"; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java new file mode 100644 index 0000000000..13309c81ed --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.xmlbeans.impl.xb.xsdschema.Public; + + +/** + * TD 物模型消息日志的数据库 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ThinkModelMessageDO { + /** + * 数据库名称 + */ + private String dataBaseName; + + // TODO @haohao:superTableName 和 tableName 是不是合并。因为每个 mapper 操作的时候,有且只会使用到其中一个。 + /** + * 超级表名称 + */ + private String superTableName; + + /** + * 表名称 + */ + private String tableName; + + /** + * 消息ID + */ + private String id; + + /** + * 扩展功能的参数 + */ + private Object sys; + + /** + * 请求方法 例如:thing.event.property.post + */ + private String method; + + /** + * 请求参数 + */ + private Object params; + + /** + * 属性上报时间戳 + */ + private Long time; + + + /** + * 设备 key + */ + private String deviceKey; + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java new file mode 100644 index 0000000000..4eb59975d8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessageDO; +import com.baomidou.dynamic.datasource.annotation.DS; +import org.apache.ibatis.annotations.Mapper; + +/** + * 处理 TD 中物模型消息日志的操作 + */ +@Mapper +@DS("tdengine") +public interface TdThinkModelMessageMapper { + + /** + * 创建物模型消息日志超级表超级表 + * + */ + @TenantIgnore + void createSuperTable(ThinkModelMessageDO superTable); + + /** + * 创建子表 + * + */ + @TenantIgnore + void createTableWithTag(ThinkModelMessageDO table); +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java new file mode 100644 index 0000000000..76cde71be7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.mq.producer.simulatesend; + +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * SimulateSend 模拟设备上报的 Producer + * + * @author alwayssuper + * @since 2024/12/17 16:35 + */ +@Slf4j +@Component +public class SimulateSendProducer { + @Resource + private ApplicationContext applicationContext; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index b26c2c123b..f26c964522 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.service.tdengine.IotThinkModelMessageService; import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; @@ -31,11 +32,12 @@ public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; - @Resource @Lazy private IotProductThinkModelService thinkModelFunctionService; - + @Resource + @Lazy + private IotThinkModelMessageService thinkModelMessageService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { // 1. 生成 ProductKey @@ -114,8 +116,10 @@ public class IotProductServiceImpl implements IotProductService { IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { - // 3.1 创建超级表数据模型 + // 3.1 创建产品超级表数据模型 thinkModelFunctionService.createSuperTableDataModel(id); + // 3.2 创建物模型日志超级表数据模型 + thinkModelMessageService.createSuperTable(id); } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java index 67ee2b99ae..67df7045fb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageService.java @@ -15,4 +15,12 @@ public interface IotThinkModelMessageService { * @param thingModelMessage 物模型消息 */ void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage); + + /** + * 创建物模型消息日志超级表 + * + * @param productId 产品编号 + */ + void createSuperTable(Long productId); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java index 3c2debfa49..3c6059f992 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThinkModelMessageServiceImpl.java @@ -7,19 +7,20 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdThinkModelMessageMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.thinkmodel.IotProductThinkModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -54,19 +55,29 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ private TdEngineDDLMapper tdEngineDDLMapper; @Resource private TdEngineDMLMapper tdEngineDMLMapper; - + @Resource + private IotProductService productService; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + @Resource + private IotTdDatabaseUtils iotTdDatabaseUtils; + + @Resource + private TdThinkModelMessageMapper tdThinkModelMessageMapper; + // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @TenantIgnore public void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage) { // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { + // 1.1 创建设备表 createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); + // 1.2 创建物模型日志设备表 + createThinkModelMessageDeviceTable(device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); } // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 @@ -90,6 +101,22 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ .build()); } + @Override + @TenantIgnore + public void createSuperTable(Long productId) { + // 1. 查询产品 + IotProductDO product = productService.getProduct(productId); + + // 2. 获取超级表的名称和数据库名称 + String databaseName = iotTdDatabaseUtils.getDatabaseName(); + String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(product.getProductKey()); + + // 3. 创建超级表 + tdThinkModelMessageMapper.createSuperTable(ThinkModelMessageDO.builder().build() + .setDataBaseName(databaseName) + .setSuperTableName(superTableName)); + } + private List getValidFunctionList(String productKey) { return iotProductThinkModelService .getProductThinkModelListByProductKey(productKey) @@ -188,6 +215,30 @@ public class IotThinkModelMessageServiceImpl implements IotThinkModelMessageServ .setTags(tagsFieldValues)); } + /** + * 创建物模型日志设备数据表 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @param deviceKey 设备 Key + * + */ + private void createThinkModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){ + + // 1. 获取超级表的名称、数据库名称、设备日志表名称 + String databaseName = iotTdDatabaseUtils.getDatabaseName(); + String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(productKey); + String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThinkModelMessageDeviceTableName(productKey, deviceName); + + // 2. 创建物模型日志设备数据表 + tdThinkModelMessageMapper.createTableWithTag(ThinkModelMessageDO.builder().build() + .setDataBaseName(databaseName) + .setSuperTableName(superTableName) + .setTableName(thinkModelMessageDeviceTableName) + .setDeviceKey(deviceKey)); + + } + /** * 获取数据库名称 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java new file mode 100644 index 0000000000..b928ec7169 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.util; + +import cn.iocoder.yudao.module.iot.enums.IotConstants; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * TD数据库工具类 + * + * @author AlwaysSuper + */ +@Component +public class IotTdDatabaseUtils { + + @Value("${spring.datasource.dynamic.datasource.tdengine.url}") + private String url; + + /** + * 获取数据库名称 + */ + public String getDatabaseName() { + int index = url.lastIndexOf("/"); + return index != -1 ? url.substring(index + 1) : url; + } + + /** + * 获取产品超级表表名 + * + * @param deviceType 设备类型 + * @param productKey 产品 Key + * @return 产品超级表表名 + */ + public static String getProductSuperTableName(Integer deviceType, String productKey) { + return switch (deviceType) { + case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); + case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); + default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); + }; + } + + /** + * 获取物模型日志超级表表名 + * + * @param productKey 产品 Key + * @return 物模型日志超级表表名 + * + */ + public static String getThinkModelMessageSuperTableName(String productKey) { + return String.format("thing_model_message_", productKey).toLowerCase(); + } + + /** + * 获取物模型日志设备表名 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @return 物模型日志设备表名 + */ + public static String getThinkModelMessageDeviceTableName(String productKey, String deviceName) { + return String.format(IotConstants.THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml new file mode 100644 index 0000000000..476ca97abc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + CREATE STABLE ${dataBaseName}.${superTableName}( + ts TIMESTAMP, + id VARCHAR(255), + sys VARCHAR(2048), + method VARCHAR(255), + params VARCHAR(2048) + )TAGS ( + deviceKey VARCHAR(255) + ) + + + + + CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} + USING ${dataBaseName}.${superTableName}( + ts, + id , + sys , + method , + params + )TAGS( + #{deviceKey} + ) + + \ No newline at end of file From caa7198e8a0d384df1c24c3e2d616e086d4eadfa Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 19 Dec 2024 21:02:36 +0800 Subject: [PATCH 041/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E8=AF=84=E5=AE=A1=20ThingModel?= =?UTF-8?q?=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/dal/dataobject/tdengine/FieldParser.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 637b2228be..9157920e0c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -34,11 +34,9 @@ public class FieldParser { * @return TdField对象 */ public static TdFieldDO parse(ThingModelProperty property) { + // 将物模型字段类型映射为 td 字段类型 String fieldName = property.getIdentifier().toLowerCase(); - - // 将物模型字段类型映射为td字段类型 String fType = TYPE_MAPPING.get(property.getDataType().toUpperCase()); - // 如果字段类型为NCHAR,默认长度为64 int dataLength = 0; if ("NCHAR".equals(fType)) { @@ -54,6 +52,7 @@ public class FieldParser { * @return 字段列表 */ public static List parse(ThingModelRespVO thingModel) { + // TODO @puhui999:是不是使用 convertList return thingModel.getModel().getProperties().stream() .map(FieldParser::parse) .collect(Collectors.toList()); @@ -66,14 +65,12 @@ public class FieldParser { * @return 转换后的 TDengine 字段对象列表 */ public static List parse(List> rows) { + // TODO @puhui999:是不是使用 convertList return rows.stream().map(row -> { String type = row.get(1).toString().toUpperCase(); + // TODO @puhui999:"NCHAR" 最好枚举下 int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; - return new TdFieldDO( - row.get(0).toString(), - type, - dataLength - ); + return new TdFieldDO(row.get(0).toString(), type, dataLength); }).collect(Collectors.toList()); } From 95067fd6c60da1f3d511bdaafc6100c4bf0e3593 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 19 Dec 2024 21:12:07 +0800 Subject: [PATCH 042/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E8=AF=84=E5=AE=A1=20ThingModel?= =?UTF-8?q?=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/vo/device/IotDevicePageReqVO.java | 4 ---- .../model/dataType/ThingModelArgument.java | 3 +-- .../iot/dal/tdengine/TdThinkModelMessageMapper.java | 1 - .../iot/service/product/IotProductServiceImpl.java | 12 ++++++------ .../tdengine/IotThingModelMessageService.java | 4 ++-- .../tdengine/IotThingModelMessageServiceImpl.java | 13 +++++++------ 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java index 791ee34b60..dcea2b0ba5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDevicePageReqVO.java @@ -6,13 +6,9 @@ import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; @Schema(description = "管理后台 - IoT 设备分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class IotDevicePageReqVO extends PageParam { @Schema(description = "设备名称", example = "王五") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java index 94c4b83d51..96f51150f5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java @@ -1,13 +1,12 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodel.model.ThinkModelProperty; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; @Data @JsonIgnoreProperties(ignoreUnknown = true) -public class ThinkModelArgument { +public class ThingModelArgument { public static final String DIRECTION_INPUT = "input"; public static final String DIRECTION_OUTPUT = "output"; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java index 4eb59975d8..90f66d508c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessageDO; import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Mapper; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index dba26352ba..a53aee55d2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.tdengine.IotThinkModelMessageService; -import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; +import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -35,10 +35,10 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy // 延迟加载,解决循环依赖 - private IotProductThinkModelService thinkModelFunctionService; + private IotProductThingModelService thingModelFunctionService; @Resource @Lazy // 延迟加载,解决循环依赖 - private IotThinkModelMessageService thinkModelMessageService; + private IotThingModelMessageService thingModelMessageService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { @@ -119,9 +119,9 @@ public class IotProductServiceImpl implements IotProductService { // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建产品超级表数据模型 - thinkModelFunctionService.createSuperTableDataModel(id); + thingModelFunctionService.createSuperTableDataModel(id); // 3.2 创建物模型日志超级表数据模型 - thinkModelMessageService.createSuperTable(id); + thingModelMessageService.createSuperTable(id); } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java index 76b3dfe326..ab77eb91ac 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.tdengine; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessage; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; /** * 物模型消息 Service @@ -14,7 +14,7 @@ public interface IotThingModelMessageService { * @param device 设备 * @param thingModelMessage 物模型消息 */ - void saveThinkModelMessage(IotDeviceDO device, ThinkModelMessage thingModelMessage); + void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); /** * 创建物模型消息日志超级表 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 5870628916..9ea237ce84 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodel.IotProductThinkModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; @@ -25,7 +24,6 @@ import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.thinkmodel.IotProductThinkModelService; import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -112,6 +110,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private List getValidThingModelList(String productKey) { return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + } + @Override @TenantIgnore public void createSuperTable(Long productId) { @@ -128,11 +128,12 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .setSuperTableName(superTableName)); } - private List getValidFunctionList(String productKey) { - return iotProductThinkModelService - .getProductThinkModelListByProductKey(productKey) + private List getValidFunctionList(String productKey) { + // TODO @puhui999:使用 convertList 会好点哈 + return iotProductThingModelService + .getProductThingModelListByProductKey(productKey) .stream() - .filter(function -> IotProductThinkModelTypeEnum.PROPERTY.getType().equals(function.getType())) + .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) .toList(); } From b2434b7b41b52aefff1de1412f0ebea0064524f1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 19 Dec 2024 21:23:07 +0800 Subject: [PATCH 043/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=A8=A1=E6=8B=9F=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=8F=91=E9=80=81=E7=9A=84=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/enums/IotConstants.java | 2 +- ...lMessageDO.java => ThingModelMessageDO.java} | 14 +++++++------- .../dal/tdengine/TdThinkModelMessageMapper.java | 7 ++++--- .../simulatesend/SimulateSendProducer.java | 2 ++ .../service/product/IotProductServiceImpl.java | 2 +- .../IotThingModelMessageServiceImpl.java | 17 ++++++++++------- .../module/iot/util/IotTdDatabaseUtils.java | 11 ++++++++--- .../tdengine/TdThinkModelMessageMapper.xml | 14 +++++++------- 8 files changed, 40 insertions(+), 29 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/{ThinkModelMessageDO.java => ThingModelMessageDO.java} (85%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java index 0762ade9de..631ca24a36 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java @@ -40,6 +40,6 @@ public interface IotConstants { *

* 格式为 thing_model_message_{productKey}_{deviceName} */ - String THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s"; + String THING_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s"; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java index 13309c81ed..c57ec1c7be 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThinkModelMessageDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java @@ -4,9 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.apache.xmlbeans.impl.xb.xsdschema.Public; - +// TODO @芋艿:纠结下字段 /** * TD 物模型消息日志的数据库 */ @@ -14,7 +13,8 @@ import org.apache.xmlbeans.impl.xb.xsdschema.Public; @Builder @NoArgsConstructor @AllArgsConstructor -public class ThinkModelMessageDO { +public class ThingModelMessageDO { + /** * 数据库名称 */ @@ -32,7 +32,7 @@ public class ThinkModelMessageDO { private String tableName; /** - * 消息ID + * 消息 ID */ private String id; @@ -42,7 +42,9 @@ public class ThinkModelMessageDO { private Object sys; /** - * 请求方法 例如:thing.event.property.post + * 请求方法 + * + * 例如:thing.event.property.post */ private String method; @@ -56,11 +58,9 @@ public class ThinkModelMessageDO { */ private Long time; - /** * 设备 key */ private String deviceKey; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java index 90f66d508c..da9730e74b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThinkModelMessageDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessageDO; import com.baomidou.dynamic.datasource.annotation.DS; import org.apache.ibatis.annotations.Mapper; @@ -17,12 +17,13 @@ public interface TdThinkModelMessageMapper { * */ @TenantIgnore - void createSuperTable(ThinkModelMessageDO superTable); + void createSuperTable(ThingModelMessageDO superTable); /** * 创建子表 * */ @TenantIgnore - void createTableWithTag(ThinkModelMessageDO table); + void createTableWithTag(ThingModelMessageDO table); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java index 76cde71be7..411ddd6f2b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; +// TODO @alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块; /** * SimulateSend 模拟设备上报的 Producer * @@ -14,6 +15,7 @@ import org.springframework.stereotype.Component; @Slf4j @Component public class SimulateSendProducer { + @Resource private ApplicationContext applicationContext; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index a53aee55d2..628805a97e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -122,7 +122,7 @@ public class IotProductServiceImpl implements IotProductService { thingModelFunctionService.createSuperTableDataModel(id); // 3.2 创建物模型日志超级表数据模型 thingModelMessageService.createSuperTable(id); - } + }x productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 9ea237ce84..454a2e072d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -57,12 +57,14 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private IotProductThingModelService iotProductThingModelService; @Resource private IotDeviceService iotDeviceService; + @Resource + private IotProductService productService; + @Resource private TdEngineDDLMapper tdEngineDDLMapper; @Resource private TdEngineDMLMapper tdEngineDMLMapper; - @Resource - private IotProductService productService; + @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @@ -119,11 +121,12 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ IotProductDO product = productService.getProduct(productId); // 2. 获取超级表的名称和数据库名称 + // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 String databaseName = iotTdDatabaseUtils.getDatabaseName(); - String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(product.getProductKey()); + String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey()); // 3. 创建超级表 - tdThinkModelMessageMapper.createSuperTable(ThinkModelMessageDO.builder().build() + tdThinkModelMessageMapper.createSuperTable(ThingModelMessageDO.builder().build() .setDataBaseName(databaseName) .setSuperTableName(superTableName)); } @@ -238,16 +241,16 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 1. 获取超级表的名称、数据库名称、设备日志表名称 String databaseName = iotTdDatabaseUtils.getDatabaseName(); - String superTableName = IotTdDatabaseUtils.getThinkModelMessageSuperTableName(productKey); + String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(productKey); + // TODO @alwayssuper:最好 databaseName、superTableName、thinkModelMessageDeviceTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThinkModelMessageDeviceTableName(productKey, deviceName); // 2. 创建物模型日志设备数据表 - tdThinkModelMessageMapper.createTableWithTag(ThinkModelMessageDO.builder().build() + tdThinkModelMessageMapper.createTableWithTag(ThingModelMessageDO.builder().build() .setDataBaseName(databaseName) .setSuperTableName(superTableName) .setTableName(thinkModelMessageDeviceTableName) .setDeviceKey(deviceKey)); - } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java index b928ec7169..8feef7bf60 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java @@ -4,8 +4,10 @@ import cn.iocoder.yudao.module.iot.enums.IotConstants; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +// TODO @芋艿:可能要思索下,有没更好的处理方式 +// TODO @芋艿:怎么改成无状态 /** - * TD数据库工具类 + * TD 数据库工具类 * * @author AlwaysSuper */ @@ -19,6 +21,7 @@ public class IotTdDatabaseUtils { * 获取数据库名称 */ public String getDatabaseName() { +// TODO @alwayssuper:StrUtil.subAfter("/") int index = url.lastIndexOf("/"); return index != -1 ? url.substring(index + 1) : url; } @@ -31,6 +34,7 @@ public class IotTdDatabaseUtils { * @return 产品超级表表名 */ public static String getProductSuperTableName(Integer deviceType, String productKey) { + // TODO @alwayssuper:枚举字段,不要 1、2、3;不符合预期,抛出异常 return switch (deviceType) { case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); @@ -45,7 +49,8 @@ public class IotTdDatabaseUtils { * @return 物模型日志超级表表名 * */ - public static String getThinkModelMessageSuperTableName(String productKey) { + public static String getThingModelMessageSuperTableName(String productKey) { + // TODO @alwayssuper:是不是应该 + 拼接就好,不用 format return String.format("thing_model_message_", productKey).toLowerCase(); } @@ -57,7 +62,7 @@ public class IotTdDatabaseUtils { * @return 物模型日志设备表名 */ public static String getThinkModelMessageDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.THINK_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); + return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml index 476ca97abc..aeecd5dc1e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml @@ -4,17 +4,17 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - + CREATE STABLE ${dataBaseName}.${superTableName}( - ts TIMESTAMP, - id VARCHAR(255), - sys VARCHAR(2048), - method VARCHAR(255), - params VARCHAR(2048) + ts TIMESTAMP, + id VARCHAR(255), + sys VARCHAR(2048), + method VARCHAR(255), + params VARCHAR(2048) )TAGS ( - deviceKey VARCHAR(255) + deviceKey VARCHAR(255) ) From 767a26dd705df1bedc9aaf9001d14902d8e3791c Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 20 Dec 2024 10:18:02 +0800 Subject: [PATCH 044/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IOT:=20ThingModel=20=E8=AF=84=E5=AE=A1?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/tdengine/FieldParser.java | 13 +++++-------- .../product/IotProductServiceImpl.java | 2 +- .../IotThingModelMessageServiceImpl.java | 19 +++++-------------- .../IotProductThingModelServiceImpl.java | 6 ++---- 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index 9157920e0c..d627817791 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -5,7 +5,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelR import java.util.HashMap; import java.util.List; -import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; /** * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 @@ -52,10 +53,7 @@ public class FieldParser { * @return 字段列表 */ public static List parse(ThingModelRespVO thingModel) { - // TODO @puhui999:是不是使用 convertList - return thingModel.getModel().getProperties().stream() - .map(FieldParser::parse) - .collect(Collectors.toList()); + return convertList(thingModel.getModel().getProperties(), FieldParser::parse); } /** @@ -65,13 +63,12 @@ public class FieldParser { * @return 转换后的 TDengine 字段对象列表 */ public static List parse(List> rows) { - // TODO @puhui999:是不是使用 convertList - return rows.stream().map(row -> { + return convertList(rows, row -> { String type = row.get(1).toString().toUpperCase(); // TODO @puhui999:"NCHAR" 最好枚举下 int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; return new TdFieldDO(row.get(0).toString(), type, dataLength); - }).collect(Collectors.toList()); + }); } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 628805a97e..a53aee55d2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -122,7 +122,7 @@ public class IotProductServiceImpl implements IotProductService { thingModelFunctionService.createSuperTableDataModel(id); // 3.2 创建物模型日志超级表数据模型 thingModelMessageService.createSuperTable(id); - }x + } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 454a2e072d..568cad5020 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -7,13 +7,9 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; @@ -22,8 +18,8 @@ import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -132,12 +128,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ } private List getValidFunctionList(String productKey) { - // TODO @puhui999:使用 convertList 会好点哈 - return iotProductThingModelService - .getProductThingModelListByProductKey(productKey) - .stream() - .filter(function -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(function.getType())) - .toList(); + return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), + thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { @@ -235,9 +227,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @param productKey 产品 Key * @param deviceName 设备名称 * @param deviceKey 设备 Key - * */ - private void createThinkModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){ + private void createThinkModelMessageDeviceTable(String productKey, String deviceName, String deviceKey) { // 1. 获取超级表的名称、数据库名称、设备日志表名称 String databaseName = iotTdDatabaseUtils.getDatabaseName(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index b5c2d4eee0..ddb800eeee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -29,10 +29,10 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; -import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; /** @@ -261,9 +261,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } }); // 过滤掉没有设置 ID 的对象 - List validUpdateList = updateList.stream() - .filter(func -> func.getId() != null) - .collect(Collectors.toList()); + List validUpdateList = filterList(updateList, thingModel -> thingModel.getId() != null); // 执行批量更新 if (CollUtil.isNotEmpty(validUpdateList)) { productThingModelMapper.updateBatch(validUpdateList); From 8ff09fea018acc63b107430fe4a5c7ca1ffbed5c Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Fri, 20 Dec 2024 14:44:46 +0800 Subject: [PATCH 045/309] =?UTF-8?q?[=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96]?= =?UTF-8?q?=EF=BC=9A=E7=89=A9=E6=A8=A1=E5=9E=8B=E6=97=A5=E5=BF=97=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 1 + .../product/IotProductDeviceTypeEnum.java | 2 +- ...er.java => TdThingModelMessageMapper.java} | 2 +- .../simulatesend/SimulateSendConsumer.java | 8 ++++ .../IotThingModelMessageServiceImpl.java | 43 ++++++++++--------- .../module/iot/util/IotTdDatabaseUtils.java | 36 ++++++++++------ .../tdengine/TdThinkModelMessageMapper.xml | 6 +-- 7 files changed, 59 insertions(+), 39 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/{TdThinkModelMessageMapper.java => TdThingModelMessageMapper.java} (93%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 4539f12591..a763924b57 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -14,6 +14,7 @@ public interface ErrorCodeConstants { ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); + ErrorCode PRODUCT_DEVICE_NOT_EXISTS = new ErrorCode(1_050_001_004, "产品设备类型不存在"); // ========== 产品物模型 1-050-002-000 ============ ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java index ef1432804a..8a5aaf74bf 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java @@ -16,7 +16,7 @@ import java.util.Arrays; public enum IotProductDeviceTypeEnum implements IntArrayValuable { DIRECT(0, "直连设备"), - GATEWAY_CHILD(1, "网关子设备"), + GATEWAY_SUB(1, "网关子设备"), GATEWAY(2, "网关设备"); /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java similarity index 93% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java index da9730e74b..0a735be03a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThinkModelMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java @@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper; */ @Mapper @DS("tdengine") -public interface TdThinkModelMessageMapper { +public interface TdThingModelMessageMapper { /** * 创建物模型消息日志超级表超级表 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java new file mode 100644 index 0000000000..dfc911df52 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java @@ -0,0 +1,8 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; + +/** + * @author alwayssuper + * @date 2024/12/20 8:04 + */ +public class SimulateSendConsumer { +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 454a2e072d..87bf01d26b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -13,11 +13,9 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdThinkModelMessageMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; @@ -68,11 +66,6 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private DeviceDataRedisDAO deviceDataRedisDAO; - @Resource - private IotTdDatabaseUtils iotTdDatabaseUtils; - - @Resource - private TdThinkModelMessageMapper tdThinkModelMessageMapper; // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @@ -85,7 +78,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); // 1.2 创建物模型日志设备表 - createThinkModelMessageDeviceTable(device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); + createThingModelMessageDeviceTable(device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); } // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 @@ -122,13 +115,23 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 2. 获取超级表的名称和数据库名称 // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 - String databaseName = iotTdDatabaseUtils.getDatabaseName(); + String databaseName = IotTdDatabaseUtils.getDatabaseName(url); String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey()); + // 解析物模型,获取字段列表 + List schemaFields = List.of( + TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(), + TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(), + TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(), + TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(), + TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build() + ); + // 设置超级表的标签 + List tagsFields = List.of( + TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build() + ); // 3. 创建超级表 - tdThinkModelMessageMapper.createSuperTable(ThingModelMessageDO.builder().build() - .setDataBaseName(databaseName) - .setSuperTableName(superTableName)); + tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); } private List getValidFunctionList(String productKey) { @@ -237,20 +240,20 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * @param deviceKey 设备 Key * */ - private void createThinkModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){ + private void createThingModelMessageDeviceTable(String productKey, String deviceName, String deviceKey){ // 1. 获取超级表的名称、数据库名称、设备日志表名称 - String databaseName = iotTdDatabaseUtils.getDatabaseName(); + String databaseName = IotTdDatabaseUtils.getDatabaseName(url); String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(productKey); // TODO @alwayssuper:最好 databaseName、superTableName、thinkModelMessageDeviceTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 - String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThinkModelMessageDeviceTableName(productKey, deviceName); + String thinkModelMessageDeviceTableName = IotTdDatabaseUtils.getThingModelMessageDeviceTableName(productKey, deviceName); // 2. 创建物模型日志设备数据表 - tdThinkModelMessageMapper.createTableWithTag(ThingModelMessageDO.builder().build() - .setDataBaseName(databaseName) - .setSuperTableName(superTableName) - .setTableName(thinkModelMessageDeviceTableName) - .setDeviceKey(deviceKey)); +// tdThingModelMessageMapper.createTableWithTag(ThingModelMessageDO.builder().build() +// .setDataBaseName(databaseName) +// .setSuperTableName(superTableName) +// .setTableName(thinkModelMessageDeviceTableName) +// .setDeviceKey(deviceKey)); } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java index 8feef7bf60..e4ecf74657 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java @@ -1,9 +1,14 @@ package cn.iocoder.yudao.module.iot.util; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.iot.enums.IotConstants; +import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_DEVICE_NOT_EXISTS; + // TODO @芋艿:可能要思索下,有没更好的处理方式 // TODO @芋艿:怎么改成无状态 /** @@ -11,19 +16,14 @@ import org.springframework.stereotype.Component; * * @author AlwaysSuper */ -@Component public class IotTdDatabaseUtils { - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - /** * 获取数据库名称 */ - public String getDatabaseName() { + public static String getDatabaseName(String url) { // TODO @alwayssuper:StrUtil.subAfter("/") - int index = url.lastIndexOf("/"); - return index != -1 ? url.substring(index + 1) : url; + return StrUtil.subAfter(url, "/", true); } /** @@ -35,11 +35,19 @@ public class IotTdDatabaseUtils { */ public static String getProductSuperTableName(Integer deviceType, String productKey) { // TODO @alwayssuper:枚举字段,不要 1、2、3;不符合预期,抛出异常 - return switch (deviceType) { - case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); - case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); - default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); - }; + if (deviceType == null) { + throw exception(PRODUCT_DEVICE_NOT_EXISTS); + } + if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { + return String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); + } else if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { + return String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); + } else if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ + return String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); + } + else{ + throw exception(PRODUCT_DEVICE_NOT_EXISTS); + } } /** @@ -51,7 +59,7 @@ public class IotTdDatabaseUtils { */ public static String getThingModelMessageSuperTableName(String productKey) { // TODO @alwayssuper:是不是应该 + 拼接就好,不用 format - return String.format("thing_model_message_", productKey).toLowerCase(); + return "thing_model_message_" + productKey.toLowerCase(); } /** @@ -61,7 +69,7 @@ public class IotTdDatabaseUtils { * @param deviceName 设备名称 * @return 物模型日志设备表名 */ - public static String getThinkModelMessageDeviceTableName(String productKey, String deviceName) { + public static String getThingModelMessageDeviceTableName(String productKey, String deviceName) { return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml index aeecd5dc1e..074ae98842 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml @@ -2,7 +2,7 @@ - + @@ -14,7 +14,7 @@ method VARCHAR(255), params VARCHAR(2048) )TAGS ( - deviceKey VARCHAR(255) + device_key VARCHAR(255) ) @@ -28,7 +28,7 @@ method , params )TAGS( - #{deviceKey} + #{device_key} ) \ No newline at end of file From a2532013ec1ade71362a4361cb37aedb3b5f0519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Fri, 20 Dec 2024 18:57:40 +0800 Subject: [PATCH 046/309] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=91IoT=EF=BC=9A=20HTTP=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/disabled.txt | 0 plugins/enabled.txt | 0 ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 0 -> 9347 bytes yudao-module-iot/pom.xml | 1 - yudao-module-iot/yudao-module-iot-api/pom.xml | 11 ++ .../yudao/module/iot/api/DeviceDataApi.java | 17 ++ .../yudao/module/iot/api/ServiceRegistry.java | 34 ++++ yudao-module-iot/yudao-module-iot-biz/pom.xml | 6 + .../iot/api/device/DeviceDataApiImpl.java | 25 +++ .../yudao/module/iot/api/package-info.java | 1 + .../admin/plugininfo/Greetings.java | 41 ----- .../admin/plugininfo/PluginController.java | 20 +-- .../admin/plugininfo/WhazzupGreeting.java | 34 ---- .../vo/PluginInstanceRespVO.java | 2 - .../vo/PluginInstanceSaveReqVO.java | 1 - .../plugin/ServiceRegistryConfiguration.java | 34 ++++ .../framework/plugin/SpringConfiguration.java | 8 +- .../service/device/IotDeviceDataService.java | 3 +- .../plugininfo/PluginInfoServiceImpl.java | 40 +++-- .../product/IotProductServiceImpl.java | 2 +- .../yudao-module-iot-plugin-api/pom.xml | 30 ---- .../yudao/module/iot/api/Greeting.java | 27 --- .../yudao/module/iot/api/package-info.java | 6 - .../yudao-module-iot-plugin/pom.xml | 24 ++- .../plugin.properties | 6 + .../yudao-module-iot-demo-plugin/pom.xml | 148 ++++++++++++++++ .../src/main/assembly/assembly.xml | 31 ++++ .../yudao/module/iot/plugin/DemoPlugin.java | 77 +++++++++ .../plugin.properties | 1 + .../yudao-module-iot-http-plugin/pom.xml | 96 +++++------ .../src/main/assembly/assembly.xml | 6 - .../yudao/module/iot/plugin/HttpHandler.java | 160 ++++++++++++++++++ .../yudao/module/iot/plugin/HttpPlugin.java | 102 +++++------ 33 files changed, 690 insertions(+), 304 deletions(-) create mode 100644 plugins/disabled.txt create mode 100644 plugins/enabled.txt create mode 100644 plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin-api/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java diff --git a/plugins/disabled.txt b/plugins/disabled.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/enabled.txt b/plugins/enabled.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar new file mode 100644 index 0000000000000000000000000000000000000000..0300a632ae6d0bd042e974113a7193dc0e578232 GIT binary patch literal 9347 zcmb_h1yo#Fwyoe0q;Qwu?hxGF-8HzoySuxEpuyeU3JVZ4IKd$aZUGYDlSy|oJ?ZJ0 zzy7~#)qAy8oxRV!=heG!zq1vj!6D!PKZbm2vgSX&{C0qSJj;rz2+~Q)i7_br76t>5 zdkj1DpSx{;{2Bil0D$(-VX}g9l47FDD)h2qs&V6ReT=~1u8b%0|P93!ltsecL>XmAOG}VPW%*Dc(oz1f_R{ z;9EuNScbGx?(XiYv})-uWaKz@@%ZvMy?gxS@{*vw#A4^FymNLrKe0P5Ae7-%EVtmD zxtZHtjYg5u`+czhfTQ1^YXbhbSR>m%s_|>U^Pd40c1Cu_CXRm-i2Ub34;N!YyT1v? z`m1moJ7X7XlfOaX{SC^(&iS8^n2CR0D9L|7+FQGrSv(a21|a_Uw?LJJ4HJ2+fa&9G zS^ql%31??}2}4_BYZFI$BWpt^r%DZ2Z?rk=hwgFm`b8O_S`~WSoA4L&VEIB|)WITP zF9M8F2MQeOxFw<3xxb9jCh8G164+F_H@bfyz_1%2@z-i-Y_!z&%3WF#E>N{XI;`A$ z7`H|oHkG{nay(?(^>yuQ*VoPmH@??i4^uvX)Cg}#zJM|f=2z)VELGYGUCufzB%P)_ zIg^~9M3f?&n-iid9J6Ps>9FPZQ^C3gLJY+c+qN!5U-4=&mr@W{irG=;&3hJv8o$ku z8)V^zh}^oxASte%#k27k1%|Wkaw$t=C!~%jy_QlBXj*=rOe%w|n@=Cq;DaiO*eluSiiM7WMKZ*#DGa`S}OHt>~0 z7ZVn{qo|(CAztKvuRQ$M-2$W*M8`q7x}3 zua!WOkDh&t3hUt<B#N>X4_!xHV1uY|iIpvP!PM{yOuWj$2qYU-fn!xdrqK%+54g4M@sF~o|RAlsA?U14iudp2t$ zSniBN6?6nLP8qkst+#&9Q4q+!$+YVUR78VxAfHgGQCvSUTy)Vk@*HIvD~zl+Ie=_; zF{`v))@mkr)y1a{3Y!!h_i7yHIwY9MMhKk|9(8Rf?4L}}Qk|haEm9PGsaCeaSc(%J zPmPWDYyivmdD*V?`&m`z0FJO%sZ|K=yOwK`>zmV|cpTAu*M3I3sq?T>2nI4MLFh0SAd{*kxQE!mQ#9E7yukPxYTkQinoWLtqXFo;c*63W6?Ik>6;u@!wT4Upok}iR zoRBYTJBWqc$yF+h7x7)hcM+iqE{^-_(Vmmb_E)jdMKx)^<>aN6G-Gn!pE`9O(=7Pv zuxi2AE}3Q?w%xe2)tql&4WQYgQTqT#`G}o*eQ@gpgPPUL7ZCQj zDzUP0FX}_h6;AwnB3ki`iq?vzWW(D&>r?Cxdkf&M;Kq1b2pY+hA%x?~-QnZjsZGdWdwPeoQ`WeWmet`jO65uq zUS#TkEGvj{2jmWte7>N9eVTT#;@^lhs5mzm8MPY#8*# zwPU9!49YKMXl(4>hNpZzL)f{pU)$Kdp5F`cV;-+%R>nwXKK@k9m(RxLgbO*e%|@Y2 zD)8MD_R*=YqDBLy)^(7osYVveu$NOuL4w|d{3<6w4%fP3e%L1bDCXS36nI!BsJ*)s z)V|c!Toxx~F)p%ZBI=ULIpnyd`X$$7zMzZ#HvF^<@yr=leLe)yg;-40?>ODq)|_b+ zTbB_H;|Hyj+Frb-yVdgC7SeBpx**o4bJ z#`HnqqaNEt+1{!0aNX(4+k(n|Xj}Udg2g(kZ{?At>4TrtSZqKSp2B>36mjkeYLZmm zAq_aIlCpXt>nl?@+wLBUCesi|kL`&2IUuj2y!o{wR7B_*u9vi-nP zG;HPgr7_(|%ct(!U3Je@!hOcZ6@+Kzq7#m+lM`1;yAPLvQ3FGfnIS*7Hc>Q+VmTQ~ zW%QfOez=qOfHh1?FPpui0NphBm6M(x)vw(hdu0KK-1>{IpdR6u9iB5qWOa_I@GJM% zG;#X6y2RTP<+o2^d+(+0{h&*|r{Ta( z^5P3TeSN+|y>mTRzlVu*bJ*@T{LVeH%8BY5JmLjG?!>yfHHbDG7ExHJ!fvj_+@DF< zeIX$4trskgetvNl*_lp1se;W_M<}lTt^eL93B0+0o|WR@E5pIi3T`c_3dR);fi89# z%r|fXUTziiD|z}8C}rVu&S871d;F8VoNRu&2XYx|8b9E3;izwloiqpU^)+;XSD3UF zYVJp+ft~%4z7=JN!Qft#D!MJ;&&z{Mln0uIfU7OIhs1aXL!sghh(Ioe*~K__mxSbb zRQk_Drs~FqCF&Rb+f8ALc=Rwd8$$GlA&26NXgo>CXo#V=i18;0g0S2QgBettIffH) zNRDNR=lM06oKcgyq2)C(NFcLCmYvhAy`?P++VzhGu%Bx2#IH?r4(bbJcpwyZxOb3OEvXzS;Mqi@nNEoo zEKOdP>0vM3-Ssrow0@;KjuZ$Hf(1U~)7mJ!gV{oE-Vx%SS&3IaB*7(s%*)ob!fP~y z=bU)(6jZW~dP-{PjDs*MP^ksmz-k!vc3gqld}zEyKQn$7v2WL@w9raOa#eKnSm|s0 zs9Whb9aN0zn?gY}%zd_#L$ftFAmfH13kvmhN9*0nOAP6?b)8udi;lp!K#qWo0IvW- zPd!+k=hr+{)rf*SL&iHSXDbF)g=D&PWL$EV~6QMxUi>m(Vg15 z?wB5<@V)K6j6xEf0m?e=^DJd#9m*aNFHb2(Q@+woINj|b+{;0eS8tzb8$Z-%WkJqF zsN_d4pREk&>z$ICUTiV+TVk{f!KfqTwtumszhqx?==K}pzeb4P(!F2n_iYB&1j6PT z)LsLAe}DO@%FVBfS%$#?04^v1fbG9m3j{hMHp@SxT{oPw>P|gKg7(NDd>~DXtz{V#wCEP>yh*B zn$!D?RyW`8-_P)Sh}Ga5m^7NqBtKr^BWr{qgnj$abJ|%DM;=)JtUfixPaG@$w&8_X@;H3}ZQHNRp z;Uh=1X-<@QZ!(oRw!qp^*g>-7In<1Gz z)ei2mTIFWoXAUxka#rq+wtL@kD{%X3{O0!rzML6{r+pVQh+->kCv~7U@{A+24?Rk( zaMxi2TcT?i;5=8OZIEjc`Gms2yL|a&x;)!CB?7Z84b=q%?xGa|zY7I|prNmxhjh`I zFD@x}>8@fFdWlz{9!HjREBZzg*9WR?*mcG_wL&jKUdFdL5B6MnKj}@avO4sc#I$+n zIY$+-=oO9+K0H2QqWO@^N_Y<+N)413w?{M|b+KKGa;y(Xp*135n(3&?aPK&$*3q&f zBI4mlHiaS<$R9X2ur<7qtQC0%CVhdzGlU1X(Ca8se@yBpqWpgMG`U1Pc(1Xy9!MS+ zGD(jD4?{1l5TZcxyz%o#$Nhvm$Uom2?6=wWq%Z&g<>T8p%m4P)_<3XGsC)ULsbl-f z(aY3j0PDc$@)7}g)?kKOQYkfPSx}C7S@DVKFlvVf2P3A##-=$Lvf+)EHkD|U2CB)K zP17X<#6${?MgH@amqy0m50|(l#~F(&!;QN`cJDL3cFgZ}X7|5+!0rTJ$NnxY1SK3| zO5IkZ$z)5@Ig)NkrK?73VNcv9)h99NqFm~iUwtiHgmPKr;c1 z4hJ6sQXtb1?75~J-fa|q`mEcrcJKk8*YbpVra@Z)=}HVBU+>` z6A|Cm(BKmOW^Q1&ta!N$NAfi++Sddh-)t+%`MRvu)k>@EjniSKMs3x^DqG4-6HCeE z-Qse*^l|aBb0eLDtMV$Xlu7L5;a3~wu)2%<%Kog|2XpYNv#UO6E+rKy9hKx8fDh}) zuvXXv9#}y*sw9}6COnZY@U0FxKmL`06TieBL211XS7P z;LlU%Sa%dIu;z6bz*U*yYN(;-;2GpDe6H6UUroTb$#r< zPn5p5&Gogn*}n&sZ@GQMCnRX+x2I#4BX4OJWs54CXVtHyadT8AK~0oDhbHLBYY2N>dJFM$C)21xbpvJEv`w zPI^mWmVC-A%xdV&n?5MhG7HL-Xx2~N(u%;Fgn2$e&p`8{j3!VG+vXZa`1MSJvyocd zqWLB8yka*3cwN}uLnDRUWS_B&(j~Vg*Nm3bZd;x2Qc^sE03wwtu*KgeA0kdoDQT=U zCiXA54&6VoOO(&^oH`RfO(cRK6TxPeEsfAhm2Oo-jkMco9XST5bddVOl(|kuzmI)B z4v(KVj7MRrjl?jbfT6h*Py>N8!@XDHNL!iIp5*XNXq=#OX%8spHp)0D8bnM7QsC6L z8`~09RjJ72oaW3bP&ftioa%9-+>S zbpL61hUZ<7dO8Pv0p`a%oQTh+(S|H3qk*F{qPbzmn_eh-c{D~2a@Ze-kVK>}GI3OO z%q7P;uU`mz=t@f~kBEHKE=|Hz#)MMZt16O1l*#5uO-<@f}d$$XWE9*;#A3XOdI6Z3k^_C~)^{Aur)I)uhW)fUNmJH7>97_}n~G8{r_< zP%d3UH)=)uhEpfsC{vig^?6=b9OD%q@qt|5gle3cV zU*rzgaIvzm=Hg&j`jQnO33__&qgB~ZpgLc6iZ@@&MDzL&g;mRBcN6zqH)4Ijq(X`c z5)`W-@Do8HxVx?l{>X^NF!hz$H%yxKh&)W6jt$379!IRYq!U|$p3$D{yYE>MbBZVf ztsDb#48_1qVt$NPtnwG6@)+q@VzBO^I_}n-)Zv`ZSZ$K2Gvdw>4PHk~$+W7Q7Y&yS z7)Sw|-@^$g&)>*?^{G#3VACsziVOUdQ7de(JR)<*1Pf^eIQP^UnucXL5Dm8RijPA5bkT;S30-aq<{JFAzMXWLY! ztWJnXky2hYoEAvmHXgy~QWj13s|I1gM?fXf2fT_wOkV2njMonhsQH%-a9X}MO)rv@ zk&I^Of{Q`wo_tj#UZUF|PkE4*K452IS95&G^?eY2AA=ZSUm)|&%9jE9mGX{&{B=~4 zg^Yl+^&`WB*09y)6YCps_bb#2w6OOv27XXgsAMX9ky;8je7OPa8?wcF<4{Niuiz|E zJ!&&Ls6cAgf-oivviC5@#ve77+A(=FF^D%esNyVV2r7}qvV7u(cVW6ly{%Y`J_>$7 z13QkuUYaj#Gz+IbOQ}Om9zhpr=LszkuM0hSC1M?VQW?rr(qI{bzTAXFjB$W9Q0t{I zWI#YgDD(;_r!8ve@R7(X$k(`%GjkGZ0d3gRYRt7NakF zxEsWAJ35IwKkBmB$+H}ruW8bht4X|B4fNlRtovNx-T-lUPod0tV_&}Yw=Dt&=&A(E z&y3ho`%4A-AO+wTd*wr^25&qsAzAbsZ~*GMzuv~(|6oT-hn$^%x7^Szz;MX7^mXQ5 znAHP#bZ#gmoF0X=zs($=5$<*Zy<`&K7}OG2zAcSTc8hZ_F**3@JC`j`w$Pck?q1r~ zOv(V|_EW9~bUunL)Oo~BYFd^cd=ZOl*uc@|`|IcTQprR)87B|-q4Fp4%-5y!k80`R z4?X^?$Aa`II{xrjYz$pZo?7a^1{3}oY-CISo7F)7%I;4M{eN|Hc>Xi^A07nVPg8;J z(H@{Pe{9av{nF|GVHE^X?YzEzG(%V(7a;t9WVW}np?9~jo>Lo-U1kS%9V^2KRS|dc z!$gLt2_~?b`$K5H87EfP%D1T^OC+N5YVIZQ94j?PCVGqKXr=!qE#Gd2+$S1Q^lmD( z@yM0obOgtj<-6%F3Fm1+IWdx&iUs+#rh7QKp@0W9^WdJ@M)`5Bn0`oHVvq*4gQPO5 zj)iO43?$vKQv!-ICtY;HTP#dtQBLB-9%dBF*!TKfJrw4F1lFNRYlvYE>KVY>PO zSPq@y3nxVW`XPLfdX+<-onl|M;JjEf@HXceUi+yl#$wHfo+O9DwhV?0oBM7k$(MyQ zU0Gw0M&z%M&Z(X1L@4pSOH3vqee3vaIlP}UeYyP^N-W57y}9P5wY&NJvX2e2zKz#9 zpp)5ELBbhiFceISL08AtHsRSA4qaz1J3lcwLn481QMx`hf!1I~H|r>o-es9BZ0vxH z_uQl;@`E!Mk!(lnrH#(^!M3Bb-Scp~fWtdLnR1Dkk~(En?YA!QtkWdVcTB{I2z6GT z-`8XEX-ps1-1qk6$ehgRFhMG`;uh@jimwo7OB|BcFqJnFq|4Goso_Q(Z}}4qz#J^c zE-AmkAt&$<7I_CEPW2mIBs(O2%#U1ib2@rU_U_I~u9>^#cz0%Rmq4*EhWcjW<=_@9 zZS!Z?cNdYxMV)yrW(Qf{)N1;3bj}FV;prpbOjS`engSA@=L=er>Lzklw96ARW0KBy zRW^~w1D!$l=ej5U^4V~FM(0WiHf1$%X)D>8vMjVMXbn>Dg~ED+;`C=Vvw}jnlB9vQ z4Oxu-X%h{#I^6l6Ve`1)PcWEx251Ml>myDD_&o>ZSQaqH`EbAzL9E?t@}lDV*S;z$ z@nkk%yWEhGjSjx2AC?w&e&Hr>brnFdR4!hX0?Q9VPIFkw?=>Pm zj5%|`2lr$TT6trjeWj-tM71KwWe;9qsH*9GxvpoDkj# z!u1mY1*SUR0_)U7q+SG6XyLT&mv#D2Sun?QF~01AF=kF=QEW&@C;m>`*e@*eP9h9dO z$H4z(il6 yudao-module-iot-api yudao-module-iot-biz - yudao-module-iot-plugin-api yudao-module-iot-plugin 4.0.0 diff --git a/yudao-module-iot/yudao-module-iot-api/pom.xml b/yudao-module-iot/yudao-module-iot-api/pom.xml index 8a75b09bf9..a370944457 100644 --- a/yudao-module-iot/yudao-module-iot-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-api/pom.xml @@ -21,6 +21,17 @@ cn.iocoder.boot yudao-common + + + org.pf4j + pf4j-spring + + + org.slf4j + slf4j-log4j12 + + + diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java new file mode 100644 index 0000000000..2f3ef60477 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.api; + +/** + * 设备数据 API + */ +public interface DeviceDataApi { + + /** + * 保存设备数据 + * + * @param productKey 产品 key + * @param deviceName 设备名称 + * @param message 消息 + */ + void saveDeviceData(String productKey, String deviceName, String message); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java new file mode 100644 index 0000000000..5c55bd89cb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.api; + +import java.util.HashMap; +import java.util.Map; + +/** + * 服务注册表 - 插架模块使用,无法使用 Spring 注入 + */ +public class ServiceRegistry { + private static final Map, Object> services = new HashMap<>(); + + /** + * 注册服务 + * + * @param serviceClass 服务类 + * @param serviceImpl 服务实现 + * @param 服务类 + */ + public static void registerService(Class serviceClass, T serviceImpl) { + services.put(serviceClass, serviceImpl); + } + + /** + * 获得服务 + * + * @param serviceClass 服务类 + * @param 服务类 + * @return 服务实现 + */ + @SuppressWarnings("unchecked") + public static T getService(Class serviceClass) { + return (T) services.get(serviceClass); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 774d353809..c0810a183d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -81,6 +81,12 @@ org.pf4j pf4j-spring + + + org.slf4j + slf4j-log4j12 + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java new file mode 100644 index 0000000000..59ae850a1e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.api.device; + +import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; + +/** + * 设备数据 API 实现类 + */ +@Service +@Validated +public class DeviceDataApiImpl implements DeviceDataApi { + + @Resource + private IotDeviceDataService iotDeviceDataService; + + @Override + public void saveDeviceData(String productKey, String deviceName, String message) { + iotDeviceDataService.saveDeviceData(productKey, deviceName, message); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java new file mode 100644 index 0000000000..c0f3d748a1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.api; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java deleted file mode 100644 index 1b29a34475..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; - -import cn.iocoder.yudao.module.iot.api.Greeting; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.List; - -/** - * 打招呼 测试用例 - */ -@Component -public class Greetings { - - @Autowired - private List greetings; - - public Integer printGreetings() { - System.out.printf("找到扩展点的 %d 个扩展 '%s'%n", greetings.size(), Greeting.class.getName()); - for (Greeting greeting : greetings) { - System.out.println(">>> " + greeting.getGreeting()); - } - return greetings.size(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java index 4cd22bccaf..321eef9b6f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; import jakarta.annotation.Resource; import org.pf4j.spring.SpringPluginManager; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -25,12 +24,8 @@ import java.util.stream.Collectors; @RequestMapping("/iot/plugins") public class PluginController { - @Resource - private ApplicationContext applicationContext; @Resource private SpringPluginManager springPluginManager; - @Resource - private Greetings greetings; @Value("${pf4j.pluginsDir}") private String pluginsDir; @@ -73,10 +68,8 @@ public class PluginController { return ResponseEntity.ok("插件上传并加载成功"); } catch (IOException e) { - e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage()); } catch (Exception e) { - e.printStackTrace(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage()); } } @@ -120,15 +113,4 @@ public class PluginController { return ResponseEntity.ok(plugins); } - /** - * 打印问候语 - * - * @return 问候语数量 - */ - @PermitAll - @GetMapping("/printGreetings") - public ResponseEntity printGreetings() { - Integer count = greetings.printGreetings(); - return ResponseEntity.ok(count); - } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java deleted file mode 100644 index b6ca7f322b..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; - -import cn.iocoder.yudao.module.iot.api.Greeting; -import org.pf4j.Extension; -import org.springframework.stereotype.Component; - -/** - * 打招呼 测试用例 - */ -@Extension -@Component -public class WhazzupGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Whazzup"; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java index e71ff29045..92edca821d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java @@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; import com.alibaba.excel.annotation.*; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java index 8d927045d5..95db299d19 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; -import java.util.*; import jakarta.validation.constraints.*; @Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java new file mode 100644 index 0000000000..385c8aee56 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.framework.plugin; + +import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +@Slf4j +@Configuration +public class ServiceRegistryConfiguration { + + @Resource + private DeviceDataApi deviceDataApi; + + @PostConstruct + public void init() { + // 将主程序中的 DeviceDataApi 实例注册到 ServiceRegistry + ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); + log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); + } + + /** + * 定义一个标记用的 Bean,用于表示 ServiceRegistry 已初始化完成 + */ + @Bean("serviceRegistryInitializedMarker") + public Object serviceRegistryInitializedMarker() { + // 返回任意对象即可,这里返回null都可以,但最好返回个实际对象 + return new Object(); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java index db03332d90..d87a94f318 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.framework.plugin; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.Greetings; import org.pf4j.spring.SpringPluginManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -10,14 +9,9 @@ import org.springframework.context.annotation.DependsOn; public class SpringConfiguration { @Bean + @DependsOn("serviceRegistryInitializedMarker") public SpringPluginManager pluginManager() { return new SpringPluginManager(); } - @Bean - @DependsOn("pluginManager") - public Greetings greetings() { - return new Greetings(); - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java index 70fd23014e..1dd4ad666e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -20,7 +20,8 @@ public interface IotDeviceDataService { * * @param productKey 产品 key * @param deviceName 设备名称 - * @param message 消息 + * @param message 消息 + *

JSON 格式,参见 ... */ void saveDeviceData(String productKey, String deviceName, String message); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java index daa76acc63..bd7efa8d0c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java @@ -22,9 +22,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import org.springframework.web.multipart.MultipartFile; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.jar.JarEntry; @@ -220,24 +218,24 @@ public class PluginInfoServiceImpl implements PluginInfoService { pluginInfoMapper.updateById(pluginInfoDo); } - @PostConstruct - public void init() { - Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS); - } - - @SneakyThrows - private void startPlugins() { - for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { - if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { - continue; - } - log.info("start plugin:{}", pluginInfoDO.getPluginId()); - try { - pluginManager.startPlugin(pluginInfoDO.getPluginId()); - } catch (Exception e) { - log.error("start plugin error", e); - } - } - } +// @PostConstruct +// public void init() { +// Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS); +// } +// +// @SneakyThrows +// private void startPlugins() { +// for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { +// if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { +// continue; +// } +// log.info("start plugin:{}", pluginInfoDO.getPluginId()); +// try { +// pluginManager.startPlugin(pluginInfoDO.getPluginId()); +// } catch (Exception e) { +// log.error("start plugin error", e); +// } +// } +// } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 628805a97e..a53aee55d2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -122,7 +122,7 @@ public class IotProductServiceImpl implements IotProductService { thingModelFunctionService.createSuperTableDataModel(id); // 3.2 创建物模型日志超级表数据模型 thingModelMessageService.createSuperTable(id); - }x + } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml b/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml deleted file mode 100644 index 6d5eb765b1..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin-api/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - 4.0.0 - cn.iocoder.boot - yudao-module-iot-plugin-api - 0.0.1 - jar - - ${project.artifactId} - - 物联网 模块插件 API,暴露给其它模块调用 - - - - 0.9.0 - - - - - - org.pf4j - pf4j-spring - ${pf4j-spring.version} - provided - - - - diff --git a/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java b/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java deleted file mode 100644 index b284549373..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package cn.iocoder.yudao.module.iot.api; - -import org.pf4j.ExtensionPoint; - -/** - * @author Decebal Suiu - */ -public interface Greeting extends ExtensionPoint { - - String getGreeting(); - -} diff --git a/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java b/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java deleted file mode 100644 index 7da0c665ba..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 占位 - * - * TODO 芋艿:后续删除 - */ -package cn.iocoder.yudao.module.iot.api; diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/pom.xml index 8ec68638f3..c8f0ff0fe8 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/pom.xml @@ -2,19 +2,29 @@ - 4.0.0 - cn.iocoder.boot - yudao-module-iot-plugin - 0.0.1 - pom - + + + + + + + yudao-module-iot + cn.iocoder.boot + ${revision} + + yudao-module-iot-demo-plugin yudao-module-iot-http-plugin + 4.0.0 + + yudao-module-iot-plugin + pom + ${project.artifactId} - 物联网模块 - 插件模块 + 物联网 插件 模块 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties new file mode 100644 index 0000000000..5a67270bb0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties @@ -0,0 +1,6 @@ +plugin.id=demo-plugin +plugin.class=cn.iocoder.yudao.module.iot.plugin.DemoPlugin +plugin.version=0.0.1 +plugin.provider=ahh +plugin.dependencies= +plugin.description=demo-plugin diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml new file mode 100644 index 0000000000..3d58a1a75e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml @@ -0,0 +1,148 @@ + + + + yudao-module-iot-plugin + cn.iocoder.boot + ${revision} + + 4.0.0 + jar + + yudao-module-iot-demo-plugin + + ${project.artifactId} + + 物联网 插件模块 - demo 插件 + + + + + demo-plugin + cn.iocoder.yudao.module.iot.plugin.DemoPlugin + 0.0.1 + ahh + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + unzip jar file + package + + + + + + + run + + + + + + + maven-assembly-plugin + 2.3 + + + + src/main/assembly/assembly.xml + + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.dependencies} + + + + + + + maven-deploy-plugin + + true + + + + + + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + provided + + + + org.pf4j + pf4j-spring + provided + + + + cn.iocoder.boot + yudao-module-iot-api + ${revision} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..daec9e4315 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml @@ -0,0 +1,31 @@ + + plugin + + zip + + false + + + false + runtime + lib + + *:jar:* + + + + + + + target/plugin-classes + classes + + + diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java new file mode 100644 index 0000000000..c97a5b9b5e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import com.sun.net.httpserver.HttpServer; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * 一个启动 HTTP 服务器的简单插件。 + */ +@Slf4j +public class DemoPlugin extends Plugin { + + private HttpServer server; + + public DemoPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + log.info("Demo 插件启动"); + // for testing the development mode + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + log.info("DemoPlugin in DEVELOPMENT mode"); + } + startDemoServer(); + } + + @Override + public void stop() { + log.info("Demo 插件停止"); + stopDemoServer(); + } + + private void startDemoServer() { + try { + server = HttpServer.create(new InetSocketAddress(9081), 0); + server.createContext("/", exchange -> { + String response = "Hello from DemoPlugin"; + exchange.sendResponseHeaders(200, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + }); + server.setExecutor(null); + server.start(); + log.info("HTTP 服务器启动成功,端口为 9081"); + log.info("访问地址为 http://127.0.0.1:9081/"); + } catch (IOException e) { + log.error("HTTP 服务器启动失败", e); + } + } + + private void stopDemoServer() { + if (server != null) { + server.stop(0); + log.info("HTTP 服务器停止成功"); + } + } + +// @Extension +// public static class WelcomeGreeting implements Greeting { +// +// @Override +// public String getGreeting() { +// return "Welcome to DemoPlugin"; +// } +// +// } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties index 694c97ba5f..4e1199acfc 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties @@ -3,3 +3,4 @@ plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpPlugin plugin.version=0.0.1 plugin.provider=ahh plugin.dependencies= +plugin.description=http-plugin-0.0.1 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index eccf47febd..200a451b62 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -3,14 +3,20 @@ xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + yudao-module-iot-plugin + cn.iocoder.boot + ${revision} + 4.0.0 - cn.iocoder.boot - yudao-module-iot-http-plugin - 0.0.1 jar + yudao-module-iot-http-plugin + ${project.artifactId} - 物联网 模块 - http 插件 + + 物联网 插件模块 - http 插件 + @@ -19,50 +25,8 @@ 0.0.1 ahh - - - 17 - ${java.version} - ${java.version} - 1.6 - 2.3 - 2.4 - 0.9.0 - - 1.18.34 - 3.3.1 - UTF-8 - - - - org.springframework.boot - spring-boot-starter-web - ${spring.boot.version} - provided - - - - org.pf4j - pf4j-spring - ${pf4j-spring.version} - provided - - - - cn.iocoder.boot - yudao-module-iot-plugin-api - ${project.version} - - - org.projectlombok - lombok - ${lombok.version} - provided - - - + + org.springframework.boot + spring-boot-starter-web + + + + org.pf4j + pf4j-spring + provided + + + + cn.iocoder.boot + yudao-module-iot-api + ${revision} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + io.netty + netty-all + 4.1.63.Final + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml index ce2e92cf95..daec9e4315 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml @@ -1,9 +1,3 @@ - plugin diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java new file mode 100644 index 0000000000..c145182eda --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java @@ -0,0 +1,160 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http.*; +import io.netty.util.CharsetUtil; + +/** + * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 + *

+ * 请求格式: + * POST /sys/{productKey}/{deviceName}/thing/event/property/post + * 请求体为 JSON 格式数据。 + *

+ * 返回结果为 JSON 格式,包含统一的 code、data、id、message、method、version 字段。 + */ +public class HttpHandler extends SimpleChannelInboundHandler { + + private final DeviceDataApi deviceDataApi; + + public HttpHandler(DeviceDataApi deviceDataApi) { + this.deviceDataApi = deviceDataApi; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { + String uri = request.uri(); + + // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post + // 使用 "/" 拆分路径 + String[] parts = uri.split("/"); + + /* + 拆分结果示例: + parts[0] = "" + parts[1] = "sys" + parts[2] = productKey + parts[3] = deviceName + parts[4] = "thing" + parts[5] = "event" + parts[6] = "property" + parts[7] = "post" + */ + boolean isCorrectPath = parts.length == 8 + && "sys".equals(parts[1]) + && "thing".equals(parts[4]) + && "event".equals(parts[5]) + && "property".equals(parts[6]) + && "post".equals(parts[7]); + + if (!isCorrectPath) { + // 如果路径不匹配,返回 404 错误 + writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); + return; + } + + String productKey = parts[2]; + String deviceName = parts[3]; + + // 从请求中获取原始数据 + String requestBody = request.content().toString(CharsetUtil.UTF_8); + + // 尝试解析请求数据为 JSON 对象 + JSONObject jsonData; + try { + jsonData = JSONUtil.parseObj(requestBody); + } catch (Exception e) { + // 数据不是合法的 JSON 格式,返回 400 错误 + JSONObject res = createResponseJson( + 400, + new JSONObject(), + null, + "请求数据不是合法的 JSON 格式: " + e.getMessage(), + "thing.event.property.post", + "1.0" + ); + writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); + return; + } + + // 获取请求中的 id 字段,若不存在则为 null + String id = jsonData.getStr("id", null); + + try { + // 调用主程序的接口保存数据 + deviceDataApi.saveDeviceData(productKey, deviceName, jsonData.toString()); + + // 构造成功响应内容 + JSONObject successRes = createResponseJson( + 200, + new JSONObject(), + id, + "success", + "thing.event.property.post", + "1.0" + ); + writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); + } catch (Exception e) { + // 保存数据过程中出现异常,返回 500 错误 + JSONObject errorRes = createResponseJson( + 500, + new JSONObject(), + id, + "The format of result is error!", + "thing.event.property.post", + "1.0" + ); + writeResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, errorRes.toString()); + } + } + + /** + * 创建标准化的响应 JSON 对象 + * + * @param code 响应状态码(业务层面的) + * @param data 返回的数据对象(JSON) + * @param id 请求的 id(可选) + * @param message 返回的提示信息 + * @param method 返回的 method 标识 + * @param version 返回的版本号 + * @return 构造好的 JSON 对象 + */ + private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { + JSONObject res = new JSONObject(); + res.set("code", code); + res.set("data", data != null ? data : new JSONObject()); // 确保 data 不为 null + res.set("id", id); + res.set("message", message); + res.set("method", method); + res.set("version", version); + return res; + } + + /** + * 向客户端返回 HTTP 响应的辅助方法。 + * + * @param ctx 通道上下文 + * @param status HTTP 响应状态码(网络层面的) + * @param content 响应内容(JSON 字符串或其他文本) + */ + private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { + FullHttpResponse response = new DefaultFullHttpResponse( + HttpVersion.HTTP_1_1, + status, + Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) + ); + + // 设置响应头为 JSON 类型和正确的编码 + response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); + response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); + + // 发送响应并在发送完成后关闭连接 + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java index 32926be452..68311300e7 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java @@ -1,79 +1,81 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.Greeting; -import com.sun.net.httpserver.HttpServer; +import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http.*; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.StringUtils; -import org.pf4j.Extension; -import org.pf4j.Plugin; import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; +import org.pf4j.Plugin; -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; -/** - * 一个启动 HTTP 服务器的简单插件。 - */ @Slf4j public class HttpPlugin extends Plugin { - private HttpServer server; + private static final int PORT = 8092; + private final ExecutorService executorService; + private DeviceDataApi deviceDataApi; // 用于保存设备数据的 API public HttpPlugin(PluginWrapper wrapper) { super(wrapper); + this.executorService = Executors.newSingleThreadExecutor(); // 创建单线程池 } @Override public void start() { log.info("HttpPlugin.start()"); - // for testing the development mode - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { - log.info("HttpPlugin in DEVELOPMENT mode"); + + // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 + deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); + if (deviceDataApi == null) { + log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); + return; } - startHttpServer(); + + // 异步启动 Netty 服务器 + executorService.submit(this::startHttpServer); } @Override public void stop() { log.info("HttpPlugin.stop()"); - stopHttpServer(); + executorService.shutdownNow(); // 停止线程池 } + // 启动 HTTP 服务 private void startHttpServer() { + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { - server = HttpServer.create(new InetSocketAddress(9081), 0); - server.createContext("/", exchange -> { - String response = "Welcome to PF4J HTTP Server"; - exchange.sendResponseHeaders(200, response.getBytes().length); - OutputStream os = exchange.getResponseBody(); - os.write(response.getBytes()); - os.close(); - }); - server.setExecutor(null); - server.start(); - log.info("HTTP server started on port 9081"); - } catch (IOException e) { - log.error("Error starting HTTP server", e); + ServerBootstrap bootstrap = new ServerBootstrap(); + bootstrap.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) { + channel.pipeline().addLast(new HttpServerCodec()); + channel.pipeline().addLast(new HttpObjectAggregator(65536)); + // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 + channel.pipeline().addLast(new HttpHandler(deviceDataApi)); + } + }); + + // 绑定端口并启动服务器 + ChannelFuture future = bootstrap.bind(PORT).sync(); + log.info("HTTP 服务器启动成功,端口为: {}", PORT); + future.channel().closeFuture().sync(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("HTTP 服务启动中断", e); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); } } - - private void stopHttpServer() { - if (server != null) { - server.stop(0); - log.info("HTTP server stopped"); - } - } - - @Extension - public static class WelcomeGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Welcome to PF4J"; - } - - } - -} \ No newline at end of file +} From e998b0c7ebe0123972f9a3aaaf43aacdba0ff75d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 21 Dec 2024 16:28:25 +0800 Subject: [PATCH 047/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E8=AF=84=E5=AE=A1=20plugin=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-api/pom.xml | 1 + .../yudao/module/iot/api/ServiceRegistry.java | 2 ++ .../iot/api/{ => device}/DeviceDataApi.java | 2 +- yudao-module-iot/yudao-module-iot-biz/pom.xml | 1 + .../iot/api/device/DeviceDataApiImpl.java | 5 ++- .../yudao/module/iot/api/package-info.java | 5 +++ .../plugin/ServiceRegistryConfiguration.java | 7 ++-- .../service/device/IotDeviceDataService.java | 2 +- .../PluginInstanceServiceImpl.java | 1 + .../yudao/module/iot/plugin/HttpHandler.java | 33 ++++++------------- .../yudao/module/iot/plugin/HttpPlugin.java | 20 +++++++---- 11 files changed, 42 insertions(+), 37 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/{ => device}/DeviceDataApi.java (86%) diff --git a/yudao-module-iot/yudao-module-iot-api/pom.xml b/yudao-module-iot/yudao-module-iot-api/pom.xml index a370944457..d2f83b785e 100644 --- a/yudao-module-iot/yudao-module-iot-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-api/pom.xml @@ -22,6 +22,7 @@ yudao-common + org.pf4j pf4j-spring diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java index 5c55bd89cb..5603ad8d72 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java @@ -7,6 +7,7 @@ import java.util.Map; * 服务注册表 - 插架模块使用,无法使用 Spring 注入 */ public class ServiceRegistry { + private static final Map, Object> services = new HashMap<>(); /** @@ -31,4 +32,5 @@ public class ServiceRegistry { public static T getService(Class serviceClass) { return (T) services.get(serviceClass); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java index 2f3ef60477..cb747f5053 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.api; +package cn.iocoder.yudao.module.iot.api.device; /** * 设备数据 API diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index c0810a183d..19b50ec215 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -81,6 +81,7 @@ org.pf4j pf4j-spring + org.slf4j diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java index 59ae850a1e..d62c20cb54 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.api.device; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -15,11 +14,11 @@ import javax.annotation.Resource; public class DeviceDataApiImpl implements DeviceDataApi { @Resource - private IotDeviceDataService iotDeviceDataService; + private IotDeviceDataService deviceDataService; @Override public void saveDeviceData(String productKey, String deviceName, String message) { - iotDeviceDataService.saveDeviceData(productKey, deviceName, message); + deviceDataService.saveDeviceData(productKey, deviceName, message); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java index c0f3d748a1..07852180d4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java @@ -1 +1,6 @@ +/** + * 占位 + * + * TODO 芋艿:后续删除 + */ package cn.iocoder.yudao.module.iot.api; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java index 385c8aee56..3450b67fb9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.framework.plugin; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.ServiceRegistry; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; @@ -26,9 +26,10 @@ public class ServiceRegistryConfiguration { /** * 定义一个标记用的 Bean,用于表示 ServiceRegistry 已初始化完成 */ - @Bean("serviceRegistryInitializedMarker") + @Bean("serviceRegistryInitializedMarker") // TODO @haohao:1)这个名字,可以搞个 public static final 常量;2)是不是 conditionBefore 啥 public Object serviceRegistryInitializedMarker() { - // 返回任意对象即可,这里返回null都可以,但最好返回个实际对象 + // 返回任意对象即可,这里返回 null 都可以,但最好返回个实际对象 return new Object(); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java index 1dd4ad666e..1c246e2c70 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java @@ -21,7 +21,7 @@ public interface IotDeviceDataService { * @param productKey 产品 key * @param deviceName 设备名称 * @param message 消息 - *

JSON 格式,参见 ... + *

参见 JSON 格式 */ void saveDeviceData(String productKey, String deviceName, String message); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java index 405efe1636..afb05ef2f8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java @@ -13,6 +13,7 @@ import org.springframework.validation.annotation.Validated; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS; +// TODO @haohao:可以搞个 plugin 包,然后把 plugininfo、plugininstance /** * IoT 插件实例 Service 实现类 * diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java index c145182eda..6d0908683b 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -12,12 +12,9 @@ import io.netty.util.CharsetUtil; /** * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 - *

- * 请求格式: - * POST /sys/{productKey}/{deviceName}/thing/event/property/post - * 请求体为 JSON 格式数据。 - *

- * 返回结果为 JSON 格式,包含统一的 code、data、id、message、method、version 字段。 + * + * 1. 请求格式:JSON 格式,地址为 POST /sys/{productKey}/{deviceName}/thing/event/property/post + * 2. 返回结果:JSON 格式,包含统一的 code、data、id、message、method、version 字段 */ public class HttpHandler extends SimpleChannelInboundHandler { @@ -29,10 +26,9 @@ public class HttpHandler extends SimpleChannelInboundHandler { @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { - String uri = request.uri(); - // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post // 使用 "/" 拆分路径 + String uri = request.uri(); String[] parts = uri.split("/"); /* @@ -52,25 +48,19 @@ public class HttpHandler extends SimpleChannelInboundHandler { && "event".equals(parts[5]) && "property".equals(parts[6]) && "post".equals(parts[7]); - if (!isCorrectPath) { - // 如果路径不匹配,返回 404 错误 writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); return; } - String productKey = parts[2]; String deviceName = parts[3]; - // 从请求中获取原始数据 + // 从请求中获取原始数据,尝试解析请求数据为 JSON 对象 String requestBody = request.content().toString(CharsetUtil.UTF_8); - - // 尝试解析请求数据为 JSON 对象 JSONObject jsonData; try { jsonData = JSONUtil.parseObj(requestBody); } catch (Exception e) { - // 数据不是合法的 JSON 格式,返回 400 错误 JSONObject res = createResponseJson( 400, new JSONObject(), @@ -82,8 +72,6 @@ public class HttpHandler extends SimpleChannelInboundHandler { writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); return; } - - // 获取请求中的 id 字段,若不存在则为 null String id = jsonData.getStr("id", null); try { @@ -101,7 +89,6 @@ public class HttpHandler extends SimpleChannelInboundHandler { ); writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); } catch (Exception e) { - // 保存数据过程中出现异常,返回 500 错误 JSONObject errorRes = createResponseJson( 500, new JSONObject(), @@ -128,7 +115,7 @@ public class HttpHandler extends SimpleChannelInboundHandler { private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { JSONObject res = new JSONObject(); res.set("code", code); - res.set("data", data != null ? data : new JSONObject()); // 确保 data 不为 null + res.set("data", data != null ? data : new JSONObject()); res.set("id", id); res.set("message", message); res.set("method", method); @@ -137,24 +124,24 @@ public class HttpHandler extends SimpleChannelInboundHandler { } /** - * 向客户端返回 HTTP 响应的辅助方法。 + * 向客户端返回 HTTP 响应的辅助方法 * * @param ctx 通道上下文 * @param status HTTP 响应状态码(网络层面的) * @param content 响应内容(JSON 字符串或其他文本) */ private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { + // 设置响应头为 JSON 类型和正确的编码 FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, status, Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) ); - - // 设置响应头为 JSON 类型和正确的编码 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); // 发送响应并在发送完成后关闭连接 ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java index 68311300e7..70da0131ce 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.ServiceRegistry; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; @@ -18,12 +18,14 @@ import java.util.concurrent.Executors; public class HttpPlugin extends Plugin { private static final int PORT = 8092; + private final ExecutorService executorService; - private DeviceDataApi deviceDataApi; // 用于保存设备数据的 API + private DeviceDataApi deviceDataApi; public HttpPlugin(PluginWrapper wrapper) { super(wrapper); - this.executorService = Executors.newSingleThreadExecutor(); // 创建单线程池 + // 创建单线程池 + this.executorService = Executors.newSingleThreadExecutor(); } @Override @@ -44,10 +46,13 @@ public class HttpPlugin extends Plugin { @Override public void stop() { log.info("HttpPlugin.stop()"); - executorService.shutdownNow(); // 停止线程池 + // 停止线程池 + executorService.shutdownNow(); } - // 启动 HTTP 服务 + /** + * 启动 HTTP 服务 + */ private void startHttpServer() { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); @@ -56,7 +61,8 @@ public class HttpPlugin extends Plugin { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer() { + .childHandler(new ChannelInitializer<>() { + @Override protected void initChannel(Channel channel) { channel.pipeline().addLast(new HttpServerCodec()); @@ -64,6 +70,7 @@ public class HttpPlugin extends Plugin { // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 channel.pipeline().addLast(new HttpHandler(deviceDataApi)); } + }); // 绑定端口并启动服务器 @@ -78,4 +85,5 @@ public class HttpPlugin extends Plugin { workerGroup.shutdownGracefully(); } } + } From e01d03eefb3fbe5d50fb67c44384bb7f6c88162c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 21 Dec 2024 16:33:50 +0800 Subject: [PATCH 048/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E7=89=A9=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 1 - .../simulatesend/SimulateSendConsumer.java | 4 ++- .../module/iot/util/IotTdDatabaseUtils.java | 25 +++++++------------ .../tdengine/TdThinkModelMessageMapper.xml | 1 - 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index a763924b57..4539f12591 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -14,7 +14,6 @@ public interface ErrorCodeConstants { ErrorCode PRODUCT_KEY_EXISTS = new ErrorCode(1_050_001_001, "产品标识已经存在"); ErrorCode PRODUCT_STATUS_NOT_DELETE = new ErrorCode(1_050_001_002, "产品状是发布状态,不允许删除"); ErrorCode PRODUCT_STATUS_NOT_ALLOW_THING_MODEL = new ErrorCode(1_050_001_003, "产品状是发布状态,不允许操作物模型"); - ErrorCode PRODUCT_DEVICE_NOT_EXISTS = new ErrorCode(1_050_001_004, "产品设备类型不存在"); // ========== 产品物模型 1-050-002-000 ============ ErrorCode THING_MODEL_NOT_EXISTS = new ErrorCode(1_050_002_000, "产品物模型不存在"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java index dfc911df52..111cf50073 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java @@ -1,8 +1,10 @@ package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; /** + * TODO @alwayssuper:记得实现,还有类注释哈 + * * @author alwayssuper - * @date 2024/12/20 8:04 + * @since 2024/12/20 8:04 */ public class SimulateSendConsumer { } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java index e4ecf74657..a409c80692 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java @@ -1,13 +1,9 @@ package cn.iocoder.yudao.module.iot.util; +import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_DEVICE_NOT_EXISTS; // TODO @芋艿:可能要思索下,有没更好的处理方式 // TODO @芋艿:怎么改成无状态 @@ -34,20 +30,17 @@ public class IotTdDatabaseUtils { * @return 产品超级表表名 */ public static String getProductSuperTableName(Integer deviceType, String productKey) { - // TODO @alwayssuper:枚举字段,不要 1、2、3;不符合预期,抛出异常 - if (deviceType == null) { - throw exception(PRODUCT_DEVICE_NOT_EXISTS); - } + Assert.notNull(deviceType, "deviceType 不能为空"); if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { return String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } else if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { + } + if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { return String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } else if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ + } + if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ return String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); } - else{ - throw exception(PRODUCT_DEVICE_NOT_EXISTS); - } + throw new IllegalArgumentException("deviceType 不正确"); } /** @@ -58,7 +51,6 @@ public class IotTdDatabaseUtils { * */ public static String getThingModelMessageSuperTableName(String productKey) { - // TODO @alwayssuper:是不是应该 + 拼接就好,不用 format return "thing_model_message_" + productKey.toLowerCase(); } @@ -70,7 +62,8 @@ public class IotTdDatabaseUtils { * @return 物模型日志设备表名 */ public static String getThingModelMessageDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); + return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, + productKey.toLowerCase(), deviceName.toLowerCase()); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml index 074ae98842..a0cc12712f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml @@ -5,7 +5,6 @@ - CREATE STABLE ${dataBaseName}.${superTableName}( ts TIMESTAMP, From 94cfc4a1b1560ad7dffb850d5cf96609322a01b8 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 25 Dec 2024 12:15:58 +0800 Subject: [PATCH 049/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IOT:=20ThingModel=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=92=8C=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotProductThingModelAccessModeEnum.java | 2 +- ...otProductThingModelParamDirectionEnum.java | 20 +++++++ ...tProductThingModelServiceCallTypeEnum.java | 20 +++++++ ...ProductThingModelServiceEventTypeEnum.java | 21 ++++++++ .../thingmodel/model/ThingModelEvent.java | 30 +++++++++-- .../model/ThingModelInputOutputParam.java | 52 +++++++++++++++++++ .../thingmodel/model/ThingModelService.java | 35 +++++++++++-- .../model/dataType/ThingModelArgument.java | 26 ---------- 8 files changed, 169 insertions(+), 37 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java index a755018034..7fca10a467 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java @@ -4,7 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; /** - * IOT 访问方式枚举类 + * IOT 产品物模型属性读取类型枚举 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java new file mode 100644 index 0000000000..e12332b6dd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IOT 产品物模型参数是输入参数还是输出参数枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum IotProductThingModelParamDirectionEnum { + + INPUT("input"), // 输入参数 + OUTPUT("output"); // 输出参数 + + private final String direction; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java new file mode 100644 index 0000000000..81ffccc493 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IOT 产品物模型服务调用方式枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum IotProductThingModelServiceCallTypeEnum { + + ASYNC("async"), // 异步调用 + SYNC("sync"); // 同步调用 + + private final String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java new file mode 100644 index 0000000000..fbf76da77b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IOT 产品物模型事件类型枚举 + * + * @author HUIHUI + */ +@AllArgsConstructor +@Getter +public enum IotProductThingModelServiceEventTypeEnum { + + INFO("info"), // 信息 + ALERT("alert"), // 告警 + ERROR("error"); // 故障 + + private final String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 718c7b60e8..315d4d036f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -1,9 +1,15 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelServiceEventTypeEnum; import lombok.Data; + import java.util.List; +/** + * 物模型中的事件 + * + * @author HUIHUI + */ @Data public class ThingModelEvent { @@ -19,14 +25,28 @@ public class ThingModelEvent { * 事件描述 */ private String description; - + /** + * 是否是标准品类的必选事件。 + * + * - true:是 + * - false:否 + */ + private Boolean required; /** * 事件类型 * - * "info"、"alert"、"error" + * 关联枚举 {@link IotProductThingModelServiceEventTypeEnum} + */ + private String eventType; + /** + * 事件的输出参数 + * + * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 + */ + private List outputParams; + /** + * 标识设备需要执行的具体操作 */ - private String type; - private List outputData; private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java new file mode 100644 index 0000000000..56a53c868b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; + +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelParamDirectionEnum; +import lombok.Data; + +import java.util.List; + +/** + * IOT 产品物模型中的服务、事件的输入输出参数 + * + * @author HUIHUI + */ +@Data +public class ThingModelInputOutputParam { + + /** + * 参数标识符 + */ + private String identifier; + /** + * 参数名称 + */ + private String name; + /** + * 参数描述 + */ + private String description; + /** + * 用于区分输入或输出参数 + * + * 关联枚举 {@link IotProductThingModelParamDirectionEnum} + */ + private String direction; + /** + * 参数的序号。从 0 开始排序,且不能重复。 + */ + private Integer paraOrder; + /** + * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 + */ + private String dataType; + /** + * 参数值的数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 + */ + private ThingModelDataSpecs dataSpecs; + /** + * 参数值的数据类型(dataType)为列表型(enum、bool、struct)的数据规范存储在 dataSpecsList 中 + */ + private List dataSpecsList; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index ec4bd34e99..3fec2762f4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -1,9 +1,15 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelServiceCallTypeEnum; import lombok.Data; + import java.util.List; +/** + * 物模型中的服务 + * + * @author HUIHUI + */ @Data public class ThingModelService { @@ -19,15 +25,34 @@ public class ThingModelService { * 服务描述 */ private String description; - + /** + * 是否是标准品类的必选服务。 + * + * - true:是 + * - false:否 + */ + private Boolean required; /** * 调用类型 * - * "sync"、"async" + * 关联枚举 {@link IotProductThingModelServiceCallTypeEnum} */ private String callType; - private List inputData; - private List outputData; + /** + * 服务的输入参数 + * + * 输入参数定义服务调用时所需提供的信息,用于控制设备行为或执行特定任务 + */ + private List inputParams; + /** + * 服务的输出参数 + * + * 输出参数定义服务调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 + */ + private List outputParams; + /** + * 标识设备需要执行的具体操作 + */ private String method; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java deleted file mode 100644 index 96f51150f5..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; - -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import lombok.Data; - -@Data -@JsonIgnoreProperties(ignoreUnknown = true) -public class ThingModelArgument { - - public static final String DIRECTION_INPUT = "input"; - public static final String DIRECTION_OUTPUT = "output"; - - private String identifier; - private String name; - /** - * 物模型中的属性 - */ - private ThingModelProperty property; - /** - * 用于区分输入或输出参数,"input" 或 "output" - */ - private String direction; - private String description; - -} From f4e9a586e3a0805d8d2834ae027908033ec5f99a Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 25 Dec 2024 15:47:24 +0800 Subject: [PATCH 050/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IOT:=20ThingModel=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=92=8C=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/ThingModelInputOutputParam.java | 2 + .../IotProductThingModelService.java | 14 +- .../IotProductThingModelServiceImpl.java | 447 +++++++----------- 3 files changed, 183 insertions(+), 280 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java index 56a53c868b..60e37f73fb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java @@ -34,6 +34,8 @@ public class ThingModelInputOutputParam { private String direction; /** * 参数的序号。从 0 开始排序,且不能重复。 + * + * TODO 考虑要不要序号,感觉是要的, 先留一手看看 */ private Integer paraOrder; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java index 04086d0a76..2eac5e1adc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java @@ -23,6 +23,13 @@ public interface IotProductThingModelService { */ Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); + /** + * 创建超级表数据模型 + * + * @param productId 产品编号 + */ + void createSuperTableDataModel(Long productId); + /** * 更新产品物模型 * @@ -61,13 +68,6 @@ public interface IotProductThingModelService { */ PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); - /** - * 创建超级表数据模型 - * - * @param productId 产品编号 - */ - void createSuperTableDataModel(Long productId); - /** * 获得产品物模型列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index ddb800eeee..e195464f9c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -3,14 +3,10 @@ package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelInputOutputParam; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArgument; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelArrayDataSpecs; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; @@ -18,8 +14,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.*; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; @@ -31,8 +26,7 @@ import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.diffList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; /** @@ -51,7 +45,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Resource private IotProductService productService; @Resource - private IotSuperTableService dbStructureDataService; + private IotSuperTableService superTableService; @Override @Transactional(rollbackFor = Exception.class) @@ -79,6 +73,99 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ return thingModel.getId(); } + @Override + public void createSuperTableDataModel(Long productId) { + // 1. 查询产品 + IotProductDO product = productService.getProduct(productId); + + // 2. 查询产品的物模型功能列表 + List thingModelList = productThingModelMapper.selectListByProductId(productId); + + // 3. 生成 TDengine 的数据模型 + superTableService.createSuperTableDataModel(product, thingModelList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { + // 1. 校验功能是否存在 + validateProductThingModelMapperExists(updateReqVO.getId()); + + // 2. 校验功能标识符是否唯一 + validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); + + // 3. 校验产品状态,发布状态下,不允许操作功能 + validateProductStatus(updateReqVO.getProductId()); + + // 4. 更新数据库 + IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); + productThingModelMapper.updateById(thingModel); + + // 5. 如果更新的是属性,需要更新默认的事件和服务 + if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteProductThingModel(Long id) { + // 1. 校验功能是否存在 + IotProductThingModelDO thingModel = productThingModelMapper.selectById(id); + if (thingModel == null) { + throw exception(THING_MODEL_NOT_EXISTS); + } + + // 3. 校验产品状态,发布状态下,不允许操作功能 + validateProductStatus(thingModel.getProductId()); + + // 2. 删除功能 + productThingModelMapper.deleteById(id); + + // 3. 如果删除的是属性,需要更新默认的事件和服务 + if (Objects.equals(thingModel.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); + } + } + + @Override + public IotProductThingModelDO getProductThingModel(Long id) { + return productThingModelMapper.selectById(id); + } + + @Override + public List getProductThingModelListByProductId(Long productId) { + return productThingModelMapper.selectListByProductId(productId); + } + + @Override + public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { + return productThingModelMapper.selectPage(pageReqVO); + } + + @Override + public List getProductThingModelListByProductKey(String productKey) { + return productThingModelMapper.selectListByProductKey(productKey); + } + + /** + * 校验功能是否存在 + * + * @param id 功能编号 + */ + private void validateProductThingModelMapperExists(Long id) { + if (productThingModelMapper.selectById(id) == null) { + throw exception(THING_MODEL_NOT_EXISTS); + } + } + + private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { + IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { + throw exception(THING_MODEL_IDENTIFIER_EXISTS); + } + } + private void validateProductStatus(Long createReqVO) { IotProductDO product = productService.getProduct(createReqVO); if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { @@ -107,99 +194,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } } - @Override - @Transactional(rollbackFor = Exception.class) - public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { - // 1. 校验功能是否存在 - validateProductThingModelMapperExists(updateReqVO.getId()); - - // 2. 校验功能标识符是否唯一 - validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); - - // 3. 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(updateReqVO.getProductId()); - - // 4. 更新数据库 - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); - productThingModelMapper.updateById(thingModel); - - // 5. 如果更新的是属性,需要更新默认的事件和服务 - if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); - } - } - - private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { - throw exception(THING_MODEL_IDENTIFIER_EXISTS); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteProductThingModel(Long id) { - // 1. 校验功能是否存在 - IotProductThingModelDO thingModel = productThingModelMapper.selectById(id); - if (thingModel == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - - // 3. 校验产品状态,发布状态下,不允许操作功能 - validateProductStatus(thingModel.getProductId()); - - // 2. 删除功能 - productThingModelMapper.deleteById(id); - - // 3. 如果删除的是属性,需要更新默认的事件和服务 - if (Objects.equals(thingModel.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { - createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); - } - } - - /** - * 校验功能是否存在 - * - * @param id 功能编号 - */ - private void validateProductThingModelMapperExists(Long id) { - if (productThingModelMapper.selectById(id) == null) { - throw exception(THING_MODEL_NOT_EXISTS); - } - } - - @Override - public IotProductThingModelDO getProductThingModel(Long id) { - return productThingModelMapper.selectById(id); - } - - @Override - public List getProductThingModelListByProductId(Long productId) { - return productThingModelMapper.selectListByProductId(productId); - } - - @Override - public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { - return productThingModelMapper.selectPage(pageReqVO); - } - - @Override - public void createSuperTableDataModel(Long productId) { - // 1. 查询产品 - IotProductDO product = productService.getProduct(productId); - - // 2. 查询产品的物模型功能列表 - List thingModelList = productThingModelMapper.selectListByProductId(productId); - - // 3. 生成 TDengine 的数据模型 - dbStructureDataService.createSuperTableDataModel(product, thingModelList); - } - - @Override - public List getProductThingModelListByProductKey(String productKey) { - return productThingModelMapper.selectListByProductKey(productKey); - } - /** * 创建默认的事件和服务 */ @@ -210,231 +204,138 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ // 2. 生成新的事件和服务列表 List newThingModelList = new ArrayList<>(); - // 生成属性上报事件 + // 2.1 生成属性上报事件 ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); if (propertyPostEvent != null) { - IotProductThingModelDO eventThingModel = buildEventThingModelDO(productId, productKey, propertyPostEvent); - newThingModelList.add(eventThingModel); + newThingModelList.add(buildEventThingModelDO(productId, productKey, propertyPostEvent)); } - // 生成属性设置服务 + // 2.2 生成属性设置服务 ThingModelService propertySetService = generatePropertySetService(propertyList); if (propertySetService != null) { - IotProductThingModelDO setServiceThingModel = buildServiceThingModelDO(productId, productKey, propertySetService); - newThingModelList.add(setServiceThingModel); + newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertySetService)); } - // 生成属性获取服务 + // 2.3 生成属性获取服务 ThingModelService propertyGetService = generatePropertyGetService(propertyList); if (propertyGetService != null) { - IotProductThingModelDO getServiceThingModel = buildServiceThingModelDO(productId, productKey, propertyGetService); - newThingModelList.add(getServiceThingModel); + newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertyGetService)); } - // 3. 获取数据库中的默认的旧事件和服务列表 + // 3.1 获取数据库中的默认的旧事件和服务列表 List oldThingModelList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) ); - - // 3.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldThingModelList, newThingModelList, - // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 - (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier()) - && Objects.equals(oldFunc.getType(), newFunc.getType())); - List createList = diffResult.get(0); // 需要新增的 - List updateList = diffResult.get(1); // 需要更新的 - List deleteList = diffResult.get(2); // 需要删除的 - - // 3.2 批量执行数据库操作 - // 新增数据库中的新事件和服务列表 - if (CollUtil.isNotEmpty(createList)) { - productThingModelMapper.insertBatch(createList); - } - // 更新数据库中的事件和服务列表 - if (CollUtil.isNotEmpty(updateList)) { - // 首先,为每个需要更新的对象设置其对应的 ID - updateList.forEach(updateFunc -> { - IotProductThingModelDO oldFunc = findThingModelByIdentifierAndType( - oldThingModelList, updateFunc.getIdentifier(), updateFunc.getType()); - if (oldFunc != null) { - updateFunc.setId(oldFunc.getId()); - } - }); - // 过滤掉没有设置 ID 的对象 - List validUpdateList = filterList(updateList, thingModel -> thingModel.getId() != null); - // 执行批量更新 - if (CollUtil.isNotEmpty(validUpdateList)) { - productThingModelMapper.updateBatch(validUpdateList); - } - } - - // 删除数据库中的旧事件和服务列表 - if (CollUtil.isNotEmpty(deleteList)) { - Set idsToDelete = CollectionUtils.convertSet(deleteList, IotProductThingModelDO::getId); - productThingModelMapper.deleteByIds(idsToDelete); - } + // 3.2 创建默认的事件和服务 + createDefaultEventsAndServices(oldThingModelList, newThingModelList); } /** - * 根据标识符和类型查找功能对象 + * 创建默认的事件和服务 */ - private IotProductThingModelDO findThingModelByIdentifierAndType(List thingModelList, - String identifier, Integer type) { - return CollUtil.findOne(thingModelList, func -> - Objects.equals(func.getIdentifier(), identifier) && Objects.equals(func.getType(), type)); + private void createDefaultEventsAndServices(List oldThingModelList, List newThingModelList) { + // 1.1 使用 diffList 方法比较新旧列表 + List> diffResult = diffList(oldThingModelList, newThingModelList, + (oldVal, newVal) -> { + // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 + boolean same = Objects.equals(oldVal.getIdentifier(), newVal.getIdentifier()) + && Objects.equals(oldVal.getType(), newVal.getType()); + if (same) { + newVal.setId(oldVal.getId()); // 设置编号 + } + return same; + }); + // 1.2 批量添加、修改、删除 + if (CollUtil.isNotEmpty(diffResult.get(0))) { + productThingModelMapper.insertBatch(diffResult.get(0)); + } + if (CollUtil.isNotEmpty(diffResult.get(1))) { + productThingModelMapper.updateBatch(diffResult.get(1)); + } + if (CollUtil.isNotEmpty(diffResult.get(2))) { + productThingModelMapper.deleteByIds(convertSet(diffResult.get(2), IotProductThingModelDO::getId)); + } } /** * 构建事件功能对象 */ private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event) { - return new IotProductThingModelDO() - .setProductId(productId) - .setProductKey(productKey) - .setIdentifier(event.getIdentifier()) - .setName(event.getName()) - .setDescription(event.getDescription()) - .setType(IotProductThingModelTypeEnum.EVENT.getType()) - .setEvent(event); + return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) + .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(event.getDescription()) + .setType(IotProductThingModelTypeEnum.EVENT.getType()).setEvent(event); } /** * 构建服务功能对象 */ private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service) { - return new IotProductThingModelDO() - .setProductId(productId) - .setProductKey(productKey) - .setIdentifier(service.getIdentifier()) - .setName(service.getName()) - .setDescription(service.getDescription()) - .setType(IotProductThingModelTypeEnum.SERVICE.getType()) - .setService(service); + return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) + .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(service.getDescription()) + .setType(IotProductThingModelTypeEnum.SERVICE.getType()).setService(service); } /** * 生成属性上报事件 */ - private ThingModelEvent generatePropertyPostEvent(List propertyList) { - if (CollUtil.isEmpty(propertyList)) { + private ThingModelEvent generatePropertyPostEvent(List thingModelList) { + // 1.1 没有属性则不生成 + if (CollUtil.isEmpty(thingModelList)) { return null; } - ThingModelEvent event = new ThingModelEvent() - .setIdentifier("post") - .setName("属性上报") - .setType("info") - .setDescription("属性上报事件") - .setMethod("thing.event.property.post"); - - // 将属性列表转换为事件的输出参数 - List outputData = new ArrayList<>(); - for (IotProductThingModelDO thingModel : propertyList) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(thingModel.getIdentifier()) - .setName(thingModel.getName()) - .setProperty(thingModel.getProperty()) - .setDescription(thingModel.getDescription()) - .setDirection("output"); // 设置为输出参数 - outputData.add(arg); - } - event.setOutputData(outputData); - return event; + // 1.2 生成属性上报事件 + return new ThingModelEvent().setIdentifier("post").setName("属性上报").setDescription("属性上报事件") + .setEventType(IotProductThingModelServiceEventTypeEnum.INFO.getType()).setMethod("thing.event.property.post") + .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); } /** * 生成属性设置服务 */ - private ThingModelService generatePropertySetService(List propertyList) { - if (propertyList == null || propertyList.isEmpty()) { + private ThingModelService generatePropertySetService(List thingModelList) { + // 1.1 过滤出所有可写属性 + thingModelList = filterList(thingModelList, thingModel -> + IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode())); + // 1.2 没有可写属性则不生成 + if (CollUtil.isEmpty(thingModelList)) { return null; } - List inputData = new ArrayList<>(); - for (IotProductThingModelDO thingModel : propertyList) { - ThingModelProperty property = thingModel.getProperty(); - if (IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(property.getAccessMode())) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - .setProperty(property) - .setDescription(property.getDescription()) - .setDirection("input"); // 设置为输入参数 - inputData.add(arg); - } - } - if (inputData.isEmpty()) { - // 如果没有可写属性,不生成属性设置服务 - return null; - } - - // 属性设置服务一般不需要输出参数 - return new ThingModelService() - .setIdentifier("set") - .setName("属性设置") - .setCallType("async") - .setDescription("属性设置服务") - .setMethod("thing.service.property.set") - .setInputData(inputData) - // 属性设置服务一般不需要输出参数 - .setOutputData(new ArrayList<>()); + // 2. 生成属性设置服务 + return new ThingModelService().setIdentifier("set").setName("属性设置").setDescription("属性设置服务") + .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()).setMethod("thing.service.property.set") + .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) + .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数 } /** * 生成属性获取服务 */ - private ThingModelService generatePropertyGetService(List propertyList) { - if (propertyList == null || propertyList.isEmpty()) { + private ThingModelService generatePropertyGetService(List thingModelList) { + // 1.1 没有属性则不生成 + if (CollUtil.isEmpty(thingModelList)) { return null; } - List outputData = new ArrayList<>(); - for (IotProductThingModelDO thingModelDO : propertyList) { - ThingModelProperty property = thingModelDO.getProperty(); - if (ObjectUtils.equalsAny(property.getAccessMode(), - IotProductThingModelAccessModeEnum.READ_ONLY.getMode(), IotProductThingModelAccessModeEnum.READ_WRITE.getMode())) { - ThingModelArgument arg = new ThingModelArgument() - .setIdentifier(property.getIdentifier()) - .setName(property.getName()) - .setProperty(property) - .setDescription(property.getDescription()) - .setDirection("output"); // 设置为输出参数 - outputData.add(arg); - } - } - if (outputData.isEmpty()) { - // 如果没有可读属性,不生成属性获取服务 - return null; - } + // 1.2 生成属性获取服务 + return new ThingModelService().setIdentifier("get").setName("属性获取").setDescription("属性获取服务") + .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()).setMethod("thing.service.property.get") + .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) + .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); + } - ThingModelService service = new ThingModelService() - .setIdentifier("get") - .setName("属性获取") - .setCallType("async") - .setDescription("属性获取服务") - .setMethod("thing.service.property.get"); - - // 定义输入参数:属性标识符列表 - ThingModelArgument inputArg = new ThingModelArgument() - .setIdentifier("properties") - .setName("属性标识符列表") - .setDescription("需要获取的属性标识符列表") - .setDirection("input"); // 设置为输入参数 - - // 创建数组类型,元素类型为文本类型(字符串)TODO @puhui999: 还得研究研究 - ThingModelArrayDataSpecs arrayType = new ThingModelArrayDataSpecs(); - arrayType.setDataType("array"); - inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) - .setDescription(inputArg.getDescription()).setDataSpecs(arrayType)); - - ThingModelDateOrTextDataSpecs textType = new ThingModelDateOrTextDataSpecs(); - textType.setDataType("text"); - inputArg.setProperty(new ThingModelProperty().setIdentifier(inputArg.getIdentifier()).setName(inputArg.getName()) - .setDescription(inputArg.getDescription()).setDataSpecs(textType)); - - service.setInputData(Collections.singletonList(inputArg)); - service.setOutputData(outputData); - return service; + /** + * 构建输入/输出参数列表 + * + * @param thingModelList 属性列表 + * @return 输入/输出参数列表 + */ + private List buildInputOutputParam(List thingModelList, + IotProductThingModelParamDirectionEnum directionEnum) { + return convertList(thingModelList, thingModel -> + BeanUtils.toBean(thingModel.getProperty(), ThingModelInputOutputParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事 + .setDirection(directionEnum.getDirection())); } } From 38796cc4d4aa0c6e485c861c4cdff0792890c6e8 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 25 Dec 2024 18:36:22 +0800 Subject: [PATCH 051/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IOT:=20ThingModel=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=92=8C=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/controller/admin/thingmodel/model/ThingModelEvent.java | 2 +- .../iot/service/thingmodel/IotProductThingModelServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 315d4d036f..0e30ad81a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -37,7 +37,7 @@ public class ThingModelEvent { * * 关联枚举 {@link IotProductThingModelServiceEventTypeEnum} */ - private String eventType; + private String type; /** * 事件的输出参数 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index e195464f9c..0592632895 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -286,7 +286,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ // 1.2 生成属性上报事件 return new ThingModelEvent().setIdentifier("post").setName("属性上报").setDescription("属性上报事件") - .setEventType(IotProductThingModelServiceEventTypeEnum.INFO.getType()).setMethod("thing.event.property.post") + .setType(IotProductThingModelServiceEventTypeEnum.INFO.getType()).setMethod("thing.event.property.post") .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); } From 1f9af15e7163c754c1a17386e15654ca7cde0d5f Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 26 Dec 2024 00:15:57 +0800 Subject: [PATCH 052/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IOT:=20ThingModel=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=92=8C=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/controller/admin/thingmodel/model/ThingModelEvent.java | 1 + .../admin/thingmodel/model/ThingModelInputOutputParam.java | 3 ++- .../controller/admin/thingmodel/model/ThingModelProperty.java | 1 + .../controller/admin/thingmodel/model/ThingModelService.java | 1 + .../service/thingmodel/IotProductThingModelServiceImpl.java | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 0e30ad81a3..55a64a268e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -24,6 +24,7 @@ public class ThingModelEvent { /** * 事件描述 */ + // TODO @puhui999: 考虑移除 private String description; /** * 是否是标准品类的必选事件。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java index 60e37f73fb..d50c6343b2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java @@ -7,7 +7,7 @@ import lombok.Data; import java.util.List; /** - * IOT 产品物模型中的服务、事件的输入输出参数 + * IOT 产品物模型中的参数 // TODO @puhui999 考虑要不改成 ThingModelParam ? * * @author HUIHUI */ @@ -25,6 +25,7 @@ public class ThingModelInputOutputParam { /** * 参数描述 */ + // TODO @puhui999: 考虑移除 private String description; /** * 用于区分输入或输出参数 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 0c9105a8b6..3a7fcbaaf0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -27,6 +27,7 @@ public class ThingModelProperty { /** * 属性描述 */ + // TODO @puhui999: 考虑移除 private String description; /** * 云端可以对该属性进行的操作类型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 3fec2762f4..8100537e1b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -24,6 +24,7 @@ public class ThingModelService { /** * 服务描述 */ + // TODO @puhui999: 考虑移除 private String description; /** * 是否是标准品类的必选服务。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index 0592632895..c87d0d5b4d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -70,6 +70,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } + // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? return thingModel.getId(); } From 064b3381df854d0613f0dac1cbc6bd45fcb0db1c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 07:55:15 +0800 Subject: [PATCH 053/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91IoT=EF=BC=9A=E5=BC=B1=E5=8C=96=20TdEngineDDLM?= =?UTF-8?q?apper=20=E5=B0=81=E8=A3=85=EF=BC=8C=E7=94=B1=E6=AF=8F=E4=B8=AA?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E7=8B=AC=E7=AB=8B=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingmodel/IotDataSpecsDataTypeEnum.java | 27 ++ .../iot/api/device/DeviceDataApiImpl.java | 4 +- .../admin/device/IotDeviceDataController.java | 4 +- .../thingmodel/model/ThingModelProperty.java | 5 +- .../ThingModelDateOrTextDataSpecs.java | 2 +- .../dal/dataobject/tdengine/FieldParser.java | 2 + .../iot/dal/dataobject/tdengine/SelectDO.java | 3 +- .../dataobject/tdengine/SelectVisualDO.java | 1 + .../dal/dataobject/tdengine/TagsSelectDO.java | 1 + .../dal/dataobject/tdengine/TdFieldDO.java | 2 + .../dal/dataobject/tdengine/TdResponse.java | 1 + .../dal/dataobject/tdengine/TdRestApi.java | 1 + .../dal/dataobject/tdengine/TdTableDO.java | 1 + .../thingmodel/IotProductThingModelDO.java | 1 + .../tdengine/IotDevicePropertyDataMapper.java | 55 ++++ .../iot/dal/tdengine/TdEngineDDLMapper.java | 1 - .../iot/emq/service/EmqxServiceImpl.java | 5 +- .../tdengine/core/TDengineTableField.java | 63 +++++ .../tdengine/core/annotation/TDengineDS.java | 17 ++ .../iot/framework/tdengine/package-info.java | 4 + ...java => IotDevicePropertyDataService.java} | 12 +- ... => IotDevicePropertyDataServiceImpl.java} | 82 +++++- .../product/IotProductServiceImpl.java | 8 +- .../tdengine/IotSuperTableService.java | 19 -- .../tdengine/IotSuperTableServiceImpl.java | 260 ------------------ .../IotProductThingModelService.java | 7 - .../IotProductThingModelServiceImpl.java | 15 - .../device/IotDevicePropertyDataMapper.xml | 45 +++ 28 files changed, 329 insertions(+), 319 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDeviceDataService.java => IotDevicePropertyDataService.java} (84%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDeviceDataServiceImpl.java => IotDevicePropertyDataServiceImpl.java} (60%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java new file mode 100644 index 0000000000..5133b00385 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.enums.thingmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * IoT 数据定义的数据类型枚举类 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum IotDataSpecsDataTypeEnum { + + INT("int"), + FLOAT("float"), + DOUBLE("double"), + ENUM("enum"), + BOOL("bool"), + TEXT("text"), + DATE("date"), + STRUCT("struct"), + ARRAY("array"); + + private final String dataType; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java index d62c20cb54..b4a2a62dba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.api.device; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -14,7 +14,7 @@ import javax.annotation.Resource; public class DeviceDataApiImpl implements DeviceDataApi { @Resource - private IotDeviceDataService deviceDataService; + private IotDevicePropertyDataService deviceDataService; @Override public void saveDeviceData(String productKey, String deviceName, String message) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 77bc78c665..a626951edb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; public class IotDeviceDataController { @Resource - private IotDeviceDataService deviceDataService; + private IotDevicePropertyDataService deviceDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 0c9105a8b6..f296cecd62 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -30,7 +30,8 @@ public class ThingModelProperty { private String description; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * + * 枚举 {@link IotProductThingModelAccessModeEnum} */ private String accessMode; /** @@ -42,6 +43,8 @@ public class ThingModelProperty { private Boolean required; /** * 数据类型,与 dataSpecs 的 dataType 保持一致 + * + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum} */ private String dataType; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java index 8d5ddff62a..9d2c88ae12 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java @@ -20,7 +20,7 @@ public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { * 数据长度,单位为字节。取值不能超过 2048。 * 当 dataType 为 text 时,需传入该参数。 */ - private Long length; + private Integer length; /** * 默认值,可选参数,用于存储默认值。 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java index d627817791..2e4e0f507e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java @@ -16,6 +16,7 @@ public class FieldParser { /** * 物模型到td数据类型映射 */ + @Deprecated private static final HashMap TYPE_MAPPING = new HashMap<>() { { put("INT", "INT"); @@ -74,6 +75,7 @@ public class FieldParser { /** * 获取字段字义 */ + @Deprecated public static String getFieldDefine(TdFieldDO field) { return "`" + field.getFieldName() + "`" + " " + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java index 542dd1e7b7..fb4cd459a6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java @@ -2,13 +2,12 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; import lombok.Data; -import java.util.Set; - // TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 /** * 查询DO */ @Data +@Deprecated public class SelectDO { // TODO @haoha:database 是个单词 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index 44acc3ac54..584562bfa6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java @@ -6,6 +6,7 @@ import java.util.Map; // TODO @haohao:类似 SelectDO 的想法,只是它是返回。ps:貌似可以在 tdengine 里面,创建一个 query 包,放这种比较特殊的查询和结果对象。dataobject 更多还是实际存储的结构化的 do @Data +@Deprecated public class SelectVisualDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java index 9fae91599d..3538308241 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java @@ -6,6 +6,7 @@ import lombok.Data; * tags查询DO */ @Data +@Deprecated public class TagsSelectDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java index 2e17515095..10c9b9fe7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +// TODO 芋艿:看看是不是后续简化掉。 /** * TD 引擎的字段 */ @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @AllArgsConstructor @Builder +@Deprecated public class TdFieldDO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java index aca5cece5b..8b64f6854d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java @@ -12,6 +12,7 @@ import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor +@Deprecated public class TdResponse { public static final int CODE_SUCCESS = 0; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java index b2d1ac0292..1bb793a45a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; */ @Slf4j @Service +@Deprecated // TODO 芋艿:貌似没用到 public class TdRestApi { @Value("${spring.datasource.dynamic.datasource.tdengine.url}") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java index 3b30352e4c..3265c3ebed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java @@ -7,6 +7,7 @@ import lombok.NoArgsConstructor; import java.util.List; +@Deprecated /** * TD 引擎的数据库 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java index 3d9e5e5fe0..f2edc5db7f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java @@ -16,6 +16,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +// TODO @huihui:IotProductThingModelDO => IotThingModelDO /** * IoT 产品物模型功能 DO *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java new file mode 100644 index 0000000000..5409189286 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.stream.Collectors; + +@Mapper +@TDengineDS +@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 +public interface IotDevicePropertyDataMapper { + + List getProductPropertySTableFieldList(@Param("productKey") String productKey); + + void createProductPropertySTable(@Param("productKey") String productKey, + @Param("fields") List fields); + + @SuppressWarnings("SimplifyStreamApiCallChains") // 保持 JDK8 兼容性 + default void alterProductPropertySTable(String productKey, + List oldFields, + List newFields) { + oldFields.removeIf(field -> TDengineTableField.FIELD_TS.equals(field.getField()) + || TDengineTableField.FIELD_DEVICE_KEY.equals(field.getField())); + List addFields = newFields.stream().filter( // 新增的字段 + newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) + .collect(Collectors.toList()); + List modifyFields = newFields.stream().filter( // 更新的字段 + newField -> oldFields.stream().anyMatch(oldField -> oldField.getField().equals(newField.getField()) + && (ObjectUtil.notEqual(oldField.getType(), newField.getType()) + || (newField.getLength() != null && ObjectUtil.notEqual(oldField.getLength(), newField.getLength()))))) + .collect(Collectors.toList()); + List dropFields = oldFields.stream().filter( // 删除的字段 + oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField()))) + .collect(Collectors.toList()); + addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field)); + // TODO 芋艿:tdengine 只允许 modify 长度;如果 type 变化,只能 drop + add + modifyFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); + dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field)); + } + + void alterProductPropertySTableAddField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + + void alterProductPropertySTableModifyField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + + void alterProductPropertySTableDropField(@Param("productKey") String productKey, + @Param("field") TDengineTableField field); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java index 3d7aa84ddd..eeb061cc55 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java @@ -96,7 +96,6 @@ public interface TdEngineDDLMapper { @TenantIgnore void modifyColumnWidthForSuperTable(TdTableDO superTable); - /** * 修改超级表 - 为超级表添加标签 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 343f9ba21c..46217a22bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.emq.service; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; @@ -21,7 +20,7 @@ import org.springframework.stereotype.Service; public class EmqxServiceImpl implements EmqxService { @Resource - private IotDeviceDataService iotDeviceDataService; + private IotDevicePropertyDataService iotDeviceDataService; // TODO 多线程处理消息 @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java new file mode 100644 index 0000000000..427c681892 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.framework.tdengine.core; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * TDEngine 表字段 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TDengineTableField { + + /** + * 字段名 - TDengine 默认 ts 字段,默认会被 TDengine 创建 + */ + public static final String FIELD_TS = "ts"; + + /** + * 字段名 - 我们系统定义的 device_key 字段,非 TDengine 默认字段 + */ + public static final String FIELD_DEVICE_KEY = "device_key"; + + public static final String TYPE_TINYINT = "TINYINT"; + public static final String TYPE_INT = "INT"; + public static final String TYPE_FLOAT = "FLOAT"; + public static final String TYPE_DOUBLE = "DOUBLE"; + public static final String TYPE_BOOL = "BOOL"; + public static final String TYPE_NCHAR = "NCHAR"; + public static final String TYPE_TIMESTAMP = "TIMESTAMP"; + + /** + * 注释 - TAG 字段 + */ + public static final String NOTE_TAG = "TAG"; + + /** + * 字段名 + */ + private String field; + + /** + * 字段类型 + */ + private String type; + + /** + * 字段长度 + */ + private Integer length; + + /** + * 注释 + */ + private String note; + + public TDengineTableField(String field, String type) { + this.field = field; + this.type = type; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java new file mode 100644 index 0000000000..e3960d026d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/annotation/TDengineDS.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation; + +import com.baomidou.dynamic.datasource.annotation.DS; + +import java.lang.annotation.*; + +/** + * TDEngine 数据源 + * + * @author 芋道源码 + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@DS("tdengine") +public @interface TDengineDS { +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java new file mode 100644 index 0000000000..f92428f7b1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/package-info.java @@ -0,0 +1,4 @@ +/** + * iot 模块的 tdengine 拓展封装 + */ +package cn.iocoder.yudao.module.iot.framework.tdengine; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java similarity index 84% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 1c246e2c70..08375cb092 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -9,11 +9,18 @@ import java.util.List; import java.util.Map; /** - * IoT 设备数据 Service 接口 + * IoT 设备【属性】数据 Service 接口 * * @author 芋道源码 */ -public interface IotDeviceDataService { +public interface IotDevicePropertyDataService { + + /** + * 定义设备属性数据的结构 + * + * @param productId 产品编号 + */ + void defineDevicePropertyData(Long productId); /** * 保存设备数据 @@ -40,4 +47,5 @@ public interface IotDeviceDataService { * @return 设备属性历史数据 */ PageResult> getHistoryDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java similarity index 60% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 4cb39c0bc5..e0de0ac3d8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -1,19 +1,27 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import jakarta.annotation.Resource; @@ -28,11 +36,32 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -@Slf4j +/** + * IoT 设备【属性】数据 Service 实现类 + * + * @author 芋道源码 + */ @Service -public class IotDeviceDataServiceImpl implements IotDeviceDataService { +@Slf4j +public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService { + + /** + * 物模型的数据类型,与 TDengine 数据类型的映射关系 + */ + private static final Map TYPE_MAPPING = MapUtil.builder() + .put(IotDataSpecsDataTypeEnum.INT.getDataType(), TDengineTableField.TYPE_INT) + .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) + .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) + .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put( IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR) + .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) + .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! + .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! + .build(); @Value("${spring.datasource.dynamic.datasource.tdengine.url}") private String url; @@ -43,12 +72,61 @@ public class IotDeviceDataServiceImpl implements IotDeviceDataService { private IotThingModelMessageService thingModelMessageService; @Resource private IotProductThingModelService thingModelService; + @Resource + private IotProductService productService; + @Resource private TdEngineDMLMapper tdEngineDMLMapper; @Resource private DeviceDataRedisDAO deviceDataRedisDAO; + @Resource + private IotDevicePropertyDataMapper devicePropertyDataMapper; + + @Override + public void defineDevicePropertyData(Long productId) { + // 1.1 查询产品和物模型 + IotProductDO product = productService.validateProductExists(productId); + List thingModels = filterList(thingModelService.getProductThingModelListByProductId(productId), + thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + // 1.2 解析 DB 里的字段 + List oldFields = new ArrayList<>(); + try { + oldFields.addAll(devicePropertyDataMapper.getProductPropertySTableFieldList(product.getProductKey())); + } catch (Exception e) { + if (!e.getMessage().contains("Table does not exist")) { + throw e; + } + } + + // 2.1 情况一:如果是新增的时候,需要创建表 + List newFields = buildTableFieldList(thingModels); + if (CollUtil.isEmpty(oldFields)) { + if (CollUtil.isEmpty(newFields)) { + log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); + return; + } + newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); + devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); + return; + } + // 2.2 情况二:如果是修改的时候,需要更新表 + devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); + } + + private List buildTableFieldList(List thingModels) { + return convertList(thingModels, thingModel -> { + TDengineTableField field = new TDengineTableField( + thingModel.getIdentifier().toLowerCase(), // TODO 芋艿:为什么要转成小写? + TYPE_MAPPING.get(thingModel.getProperty().getDataType())); + if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { + field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); + } + return field; + }); + } + @Override public void saveDeviceData(String productKey, String deviceName, String message) { // 1. 根据产品 key 和设备名称,获得设备信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index a53aee55d2..1d2e8892a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; @@ -39,6 +40,9 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy // 延迟加载,解决循环依赖 private IotThingModelMessageService thingModelMessageService; + @Resource + @Lazy // 延迟加载,解决循环依赖 + private IotDevicePropertyDataService devicePropertyDataService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { @@ -119,8 +123,8 @@ public class IotProductServiceImpl implements IotProductService { // 3. 产品是发布状态 if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建产品超级表数据模型 - thingModelFunctionService.createSuperTableDataModel(id); - // 3.2 创建物模型日志超级表数据模型 + devicePropertyDataService.defineDevicePropertyData(id); + // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分; thingModelMessageService.createSuperTable(id); } productMapper.updateById(updateObj); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java deleted file mode 100644 index bb6d8c0772..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableService.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - - -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; - -import java.util.List; - -/** - * IoT 超级表服务,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 - */ -public interface IotSuperTableService { - - /** - * 创建超级表数据模型 - */ - void createSuperTableDataModel(IotProductDO product, List thingModelList); - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java deleted file mode 100644 index 1727fae624..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotSuperTableServiceImpl.java +++ /dev/null @@ -1,260 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * IoT 超级表服务实现类,负责根据物模型创建和更新超级表,以及创建超级表的子表等操作。 - */ -@Service -@Slf4j -public class IotSuperTableServiceImpl implements IotSuperTableService { - - @Resource - private TdEngineDDLMapper tdEngineDDLMapper; - - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - - @Override - public void createSuperTableDataModel(IotProductDO product, List thingModelList) { - ThingModelRespVO thingModel = buildThingModel(product, thingModelList); - - if (thingModel.getModel() == null || CollUtil.isEmpty(thingModel.getModel().getProperties())) { - log.warn("物模型属性列表为空,不创建超级表"); - return; - } - - String superTableName = getSuperTableName(product.getDeviceType(), product.getProductKey()); - String databaseName = getDatabaseName(); - - List> results = tdEngineDDLMapper.showSuperTables(new TdTableDO(databaseName, superTableName)); - int tableExists = results == null || results.isEmpty() ? 0 : results.size(); - if (tableExists > 0) { - updateSuperTable(thingModel, product.getDeviceType()); - } else { - createSuperTable(thingModel, product.getDeviceType()); - } - } - - /** - * 创建超级表 - */ - private void createSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - // 解析物模型,获取字段列表 - List schemaFields = new ArrayList<>(); - schemaFields.add(TdFieldDO.builder() - .fieldName("time") - .dataType("TIMESTAMP") - .build()); - schemaFields.addAll(FieldParser.parse(thingModel)); - - // 设置超级表的标签 - List tagsFields = List.of( - TdFieldDO.builder().fieldName("product_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_name").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("device_type").dataType("INT").build() - ); - - // 获取超级表的名称和数据库名称 - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); - String databaseName = getDatabaseName(); - - // 创建超级表 - tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); - } - - /** - * 更新超级表 - */ - private void updateSuperTable(ThingModelRespVO thingModel, Integer deviceType) { - String superTableName = getSuperTableName(deviceType, thingModel.getProductKey()); - try { - List oldFields = getTableFields(superTableName); - List newFields = FieldParser.parse(thingModel); - - updateTableFields(superTableName, oldFields, newFields); - } catch (Exception e) { - log.error("更新物模型超级表失败: {}", e.getMessage(), e); - } - } - - /** - * 获取表的字段信息 - */ - private List getTableFields(String tableName) { - List> tableDescription = tdEngineDDLMapper.describeSuperTable(new TdTableDO(getDatabaseName(), tableName)); - if (CollUtil.isEmpty(tableDescription)) { - return Collections.emptyList(); - } - - return tableDescription.stream() - .filter(map -> !"TAG".equals(map.get("note"))) - .filter(map -> !"time".equals(map.get("field"))) - .map(map -> TdFieldDO.builder() - .fieldName((String) map.get("field")) - .dataType((String) map.get("type")) - .dataLength((Integer) map.get("length")) - .build()) - .collect(Collectors.toList()); - } - - /** - * 更新表的字段,包括新增、修改和删除字段 - */ - private void updateTableFields(String tableName, List oldFields, List newFields) { - String databaseName = getDatabaseName(); - - // 获取新增、修改、删除的字段 - List addFields = getAddFields(oldFields, newFields); - List modifyFields = getModifyFields(oldFields, newFields); - List dropFields = getDropFields(oldFields, newFields); - - // 添加新增字段 - if (CollUtil.isNotEmpty(addFields)) { - for (TdFieldDO addField : addFields) { - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(addField) - .build()); - } - } - // 删除旧字段 - if (CollUtil.isNotEmpty(dropFields)) { - for (TdFieldDO dropField : dropFields) { - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(dropField) - .build()); - } - } - // 修改字段(先删除再添加) - if (CollUtil.isNotEmpty(modifyFields)) { - for (TdFieldDO modifyField : modifyFields) { - tdEngineDDLMapper.dropColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(modifyField) - .build()); - tdEngineDDLMapper.addColumnForSuperTable(TdTableDO.builder() - .dataBaseName(databaseName) - .superTableName(tableName) - .column(modifyField) - .build()); - } - } - } - - /** - * 获取需要新增的字段 - */ - private List getAddFields(List oldFields, List newFields) { - Set oldFieldNames = oldFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return newFields.stream() - .filter(f -> !oldFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - /** - * 获取需要修改的字段 - */ - private List getModifyFields(List oldFields, List newFields) { - Map oldFieldMap = oldFields.stream() - .collect(Collectors.toMap(TdFieldDO::getFieldName, f -> f)); - - return newFields.stream() - .filter(f -> { - TdFieldDO oldField = oldFieldMap.get(f.getFieldName()); - return oldField != null && ( - !oldField.getDataType().equals(f.getDataType()) || - !Objects.equals(oldField.getDataLength(), f.getDataLength()) - ); - }) - .collect(Collectors.toList()); - } - - /** - * 获取需要删除的字段 - */ - private List getDropFields(List oldFields, List newFields) { - Set newFieldNames = newFields.stream() - .map(TdFieldDO::getFieldName) - .collect(Collectors.toSet()); - return oldFields.stream() - .filter(f -> !"time".equals(f.getFieldName())) - .filter(f -> !newFieldNames.contains(f.getFieldName())) - .collect(Collectors.toList()); - } - - /** - * 构建物模型 - */ - private ThingModelRespVO buildThingModel(IotProductDO product, List thingModelList) { - ThingModelRespVO thingModel = new ThingModelRespVO(); - thingModel.setId(product.getId()); - thingModel.setProductKey(product.getProductKey()); - - List properties = thingModelList.stream() - .filter(item -> IotProductThingModelTypeEnum.PROPERTY.equals( - IotProductThingModelTypeEnum.valueOfType(item.getType()))) - .map(this::buildThingModelProperty) - .collect(Collectors.toList()); - - ThingModelRespVO.Model model = new ThingModelRespVO.Model(); - model.setProperties(properties); - thingModel.setModel(model); - - return thingModel; - } - - /** - * 构建物模型属性 - */ - private ThingModelProperty buildThingModelProperty(IotProductThingModelDO thingModel) { - ThingModelProperty property = BeanUtil.copyProperties(thingModel, ThingModelProperty.class); - property.setDataType(thingModel.getProperty().getDataType()); - return property; - } - - /** - * 获取数据库名称 - */ - private String getDatabaseName() { - int index = url.lastIndexOf("/"); - return index != -1 ? url.substring(index + 1) : url; - } - - /** - * 获取超级表名称 - */ - private String getSuperTableName(Integer deviceType, String productKey) { - String prefix = switch (deviceType) { - case 1 -> "gateway_sub_"; - case 2 -> "gateway_"; - default -> "device_"; - }; - return (prefix + productKey).toLowerCase(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java index 04086d0a76..6df29e1018 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java @@ -61,13 +61,6 @@ public interface IotProductThingModelService { */ PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); - /** - * 创建超级表数据模型 - * - * @param productId 产品编号 - */ - void createSuperTableDataModel(Long productId); - /** * 获得产品物模型列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index ddb800eeee..a53fb69473 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotSuperTableService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -50,8 +49,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Resource private IotProductService productService; - @Resource - private IotSuperTableService dbStructureDataService; @Override @Transactional(rollbackFor = Exception.class) @@ -183,18 +180,6 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ return productThingModelMapper.selectPage(pageReqVO); } - @Override - public void createSuperTableDataModel(Long productId) { - // 1. 查询产品 - IotProductDO product = productService.getProduct(productId); - - // 2. 查询产品的物模型功能列表 - List thingModelList = productThingModelMapper.selectListByProductId(productId); - - // 3. 生成 TDengine 的数据模型 - dbStructureDataService.createSuperTableDataModel(product, thingModelList); - } - @Override public List getProductThingModelListByProductKey(String productKey) { return productThingModelMapper.selectListByProductKey(productKey); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml new file mode 100644 index 0000000000..9894e27a0e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + CREATE STABLE product_property_${productKey} + + ${field.field} ${field.type} + + (${field.length}) + + + TAGS ( + device_key NCHAR(50) + ) + + + + ALTER STABLE product_property_${productKey} + ADD COLUMN ${field.field} ${field.type} + + (${field.length}) + + + + + ALTER STABLE product_property_${productKey} + MODIFY COLUMN ${field.field} ${field.type} + + (${field.length}) + + + + + ALTER STABLE product_property_${productKey} + DROP COLUMN ${field.field} + + + \ No newline at end of file From 245ab4e62d6b29addc266f6f176f912c262c3e1b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 09:55:19 +0800 Subject: [PATCH 054/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E4=BF=AE=E5=A4=8D=20device=20?= =?UTF-8?q?=E5=BB=BA=E8=A1=A8=E6=97=B6=EF=BC=8Ctdengine=20=E5=88=86?= =?UTF-8?q?=E6=88=90=20length=20=E5=92=8C=20type=20=E7=9A=84=E6=83=85?= =?UTF-8?q?=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tdengine/IotDevicePropertyDataMapper.java | 36 +++++++++++++++---- .../IotDevicePropertyDataServiceImpl.java | 1 + .../service/device/IotDeviceServiceImpl.java | 1 + .../product/IotProductServiceImpl.java | 1 + 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index 5409189286..6053444fea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; @@ -7,6 +8,7 @@ import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -29,18 +31,38 @@ public interface IotDevicePropertyDataMapper { List addFields = newFields.stream().filter( // 新增的字段 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) .collect(Collectors.toList()); - List modifyFields = newFields.stream().filter( // 更新的字段 - newField -> oldFields.stream().anyMatch(oldField -> oldField.getField().equals(newField.getField()) - && (ObjectUtil.notEqual(oldField.getType(), newField.getType()) - || (newField.getLength() != null && ObjectUtil.notEqual(oldField.getLength(), newField.getLength()))))) - .collect(Collectors.toList()); List dropFields = oldFields.stream().filter( // 删除的字段 oldField -> newFields.stream().noneMatch(n -> n.getField().equals(oldField.getField()))) .collect(Collectors.toList()); + List modifyTypeFields = new ArrayList<>(); // 变更类型的字段 + List modifyLengthFields = new ArrayList<>(); // 变更长度的字段 + newFields.forEach(newField -> { + TDengineTableField oldField = CollUtil.findOne(oldFields, field -> field.getField().equals(newField.getField())); + if (oldField == null) { + return; + } + if (ObjectUtil.notEqual(oldField.getType(), newField.getType())) { + modifyTypeFields.add(newField); + return; + } + if (newField.getLength()!= null) { + if (newField.getLength() > oldField.getLength()) { + modifyLengthFields.add(newField); + } else if (newField.getLength() < oldField.getLength()) { + // 特殊:TDengine 长度修改时,只允许变长,所以此时认为是修改类型 + modifyTypeFields.add(newField); + } + } + }); + + // 执行 addFields.forEach(field -> alterProductPropertySTableAddField(productKey, field)); - // TODO 芋艿:tdengine 只允许 modify 长度;如果 type 变化,只能 drop + add - modifyFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); dropFields.forEach(field -> alterProductPropertySTableDropField(productKey, field)); + modifyLengthFields.forEach(field -> alterProductPropertySTableModifyField(productKey, field)); + modifyTypeFields.forEach(field -> { + alterProductPropertySTableDropField(productKey, field); + alterProductPropertySTableAddField(productKey, field); + }); } void alterProductPropertySTableAddField(@Param("productKey") String productKey, diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index e0de0ac3d8..5213decf67 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -100,6 +100,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe } } + // TODO 芋艿:建表的时候,表名要小写么? // 2.1 情况一:如果是新增的时候,需要创建表 List newFields = buildTableFieldList(thingModels); if (CollUtil.isEmpty(oldFields)) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index a43521ff92..39bac89747 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -60,6 +60,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { throw exception(PRODUCT_NOT_EXISTS); } // 1.2 校验设备标识是否唯一 + // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { throw exception(DEVICE_KEY_EXISTS); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 1d2e8892a3..6b7a95f62a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -47,6 +47,7 @@ public class IotProductServiceImpl implements IotProductService { @Override public Long createProduct(IotProductSaveReqVO createReqVO) { // 1. 生成 ProductKey + // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { throw exception(PRODUCT_KEY_EXISTS); } From 09a26666ec0036c0536ce891de7aef303ea53779 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 12:51:36 +0800 Subject: [PATCH 055/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91IoT=EF=BC=9A=E8=A7=A3=E5=86=B3=20device=20?= =?UTF-8?q?=E5=BB=BA=E8=A1=A8=E7=9A=84=E6=97=B6=E5=80=99=EF=BC=8Ctdengine?= =?UTF-8?q?=20=E9=BB=98=E8=AE=A4=E5=AD=97=E6=AE=B5=E9=83=BD=E6=98=AF?= =?UTF-8?q?=E5=B0=8F=E5=86=99=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E9=80=9A?= =?UTF-8?q?=E8=BF=87=20=5F=20=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/service/device/IotDevicePropertyDataServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 5213decf67..99c04a843b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -100,7 +100,6 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe } } - // TODO 芋艿:建表的时候,表名要小写么? // 2.1 情况一:如果是新增的时候,需要创建表 List newFields = buildTableFieldList(thingModels); if (CollUtil.isEmpty(oldFields)) { @@ -119,7 +118,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe private List buildTableFieldList(List thingModels) { return convertList(thingModels, thingModel -> { TDengineTableField field = new TDengineTableField( - thingModel.getIdentifier().toLowerCase(), // TODO 芋艿:为什么要转成小写? + StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 TYPE_MAPPING.get(thingModel.getProperty().getDataType())); if (thingModel.getProperty().getDataType().equals(IotDataSpecsDataTypeEnum.TEXT.getDataType())) { field.setLength(((ThingModelDateOrTextDataSpecs) thingModel.getProperty().getDataSpecs()).getLength()); From 7b64b7fc69ad38c4cd9bd0616e68059d0661579f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 12:57:56 +0800 Subject: [PATCH 056/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E8=B7=A8=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=20ProductKey=20=E5=92=8C=20DeviceKey?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E8=B7=A8=E7=A7=9F=E6=88=B7=E7=9A=84?= =?UTF-8?q?=20Tdengine=20=E8=A1=A8=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/dal/mysql/device/IotDeviceMapper.java | 4 +++- .../dal/mysql/product/IotProductMapper.java | 4 +++- .../service/device/IotDeviceServiceImpl.java | 10 ++++++---- .../service/product/IotProductServiceImpl.java | 18 +++++++++--------- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 893d7eefda..2ed10eee55 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; @@ -47,7 +48,8 @@ public interface IotDeviceMapper extends BaseMapperX { } default IotDeviceDO selectByDeviceKey(String deviceKey) { - return selectOne(IotDeviceDO::getDeviceKey, deviceKey); + return selectOne(new LambdaQueryWrapper() + .apply("LOWER(device_key) = {0}", deviceKey.toLowerCase())); } default List selectList(Integer deviceType) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index aef149ae72..0f56251003 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; /** @@ -23,7 +24,8 @@ public interface IotProductMapper extends BaseMapperX { } default IotProductDO selectByProductKey(String productKey) { - return selectOne(IotProductDO::getProductKey, productKey); + return selectOne(new LambdaQueryWrapper() + .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 39bac89747..1896b98b7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; @@ -60,10 +61,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { throw exception(PRODUCT_NOT_EXISTS); } // 1.2 校验设备标识是否唯一 - // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { - throw exception(DEVICE_KEY_EXISTS); - } + TenantUtils.executeIgnore(() -> { + if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); // 1.3 校验设备名称在同一产品下是否唯一 if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { throw exception(DEVICE_NAME_EXISTS); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 6b7a95f62a..d384e44c04 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -34,9 +34,6 @@ public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotProductThingModelService thingModelFunctionService; @Resource @Lazy // 延迟加载,解决循环依赖 private IotThingModelMessageService thingModelMessageService; @@ -46,11 +43,14 @@ public class IotProductServiceImpl implements IotProductService { @Override public Long createProduct(IotProductSaveReqVO createReqVO) { - // 1. 生成 ProductKey - // TODO 芋艿:校验时,需要跨租户唯一,避免 TDEngine 无法处理;并且要忽略大小写 - if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { - throw exception(PRODUCT_KEY_EXISTS); - } + // 1. 校验 ProductKey + TenantUtils.executeIgnore(() -> { + // 为什么忽略租户?避免多个租户之间,productKey 重复,导致 TDengine 设备属性表重复 + if (productMapper.selectByProductKey(createReqVO.getProductKey()) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); + // 2. 插入 IotProductDO product = BeanUtils.toBean(createReqVO, IotProductDO.class) .setStatus(IotProductStatusEnum.UNPUBLISHED.getStatus()); From fae17e91253fb8e23c6aecf955aa75603f437b15 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 26 Dec 2024 13:27:02 +0800 Subject: [PATCH 057/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IOT:=20ThingModel=20=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=92=8C=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 7 ----- .../IotProductThingModelController.http | 2 +- .../thingmodel/model/ThingModelEvent.java | 5 ---- .../model/ThingModelInputOutputParam.java | 5 ---- .../thingmodel/model/ThingModelProperty.java | 5 ---- .../thingmodel/model/ThingModelService.java | 5 ---- .../dataType/ThingModelStructDataSpecs.java | 4 --- .../IotProductThingModelService.java | 7 ----- .../IotProductThingModelServiceImpl.java | 28 ++++++++++--------- 9 files changed, 16 insertions(+), 52 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 19b50ec215..d0ed0bcacd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -25,13 +25,6 @@ ${revision} - - cn.iocoder.boot - yudao-module-iot-plugin-api - 0.0.1 - compile - - cn.iocoder.boot yudao-spring-boot-starter-biz-tenant diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http index 7742705ef1..7e7aa05d6e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http @@ -176,7 +176,7 @@ tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} ### 请求 /iot/product-thing-model/get 接口 => 成功 -GET {{baseUrl}}/iot/product-thing-model/get?id=40 +GET {{baseUrl}}/iot/product-thing-model/get?id=67 tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 55a64a268e..f49f5f86e6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -21,11 +21,6 @@ public class ThingModelEvent { * 事件名称 */ private String name; - /** - * 事件描述 - */ - // TODO @puhui999: 考虑移除 - private String description; /** * 是否是标准品类的必选事件。 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java index d50c6343b2..3b7b1414fa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java @@ -22,11 +22,6 @@ public class ThingModelInputOutputParam { * 参数名称 */ private String name; - /** - * 参数描述 - */ - // TODO @puhui999: 考虑移除 - private String description; /** * 用于区分输入或输出参数 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 0865f403c9..bb7376fc8c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -24,11 +24,6 @@ public class ThingModelProperty { * 属性名称 */ private String name; - /** - * 属性描述 - */ - // TODO @puhui999: 考虑移除 - private String description; /** * 云端可以对该属性进行的操作类型 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 8100537e1b..0bdf498d3c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -21,11 +21,6 @@ public class ThingModelService { * 服务名称 */ private String name; - /** - * 服务描述 - */ - // TODO @puhui999: 考虑移除 - private String description; /** * 是否是标准品类的必选服务。 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java index e0f5bb5397..5e31d3996a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java @@ -25,10 +25,6 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { * 属性名称 */ private String name; - /** - * 属性描述 - */ - private String description; /** * 云端可以对该属性进行的操作类型 * 关联枚举 {@link IotProductThingModelAccessModeEnum} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java index 2eac5e1adc..6df29e1018 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java @@ -23,13 +23,6 @@ public interface IotProductThingModelService { */ Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); - /** - * 创建超级表数据模型 - * - * @param productId 产品编号 - */ - void createSuperTableDataModel(Long productId); - /** * 更新产品物模型 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index 90ea27ca00..3dc5790a5e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -193,17 +193,17 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ // 2.1 生成属性上报事件 ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); if (propertyPostEvent != null) { - newThingModelList.add(buildEventThingModelDO(productId, productKey, propertyPostEvent)); + newThingModelList.add(buildEventThingModelDO(productId, productKey, propertyPostEvent, "属性上报事件")); } // 2.2 生成属性设置服务 ThingModelService propertySetService = generatePropertySetService(propertyList); if (propertySetService != null) { - newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertySetService)); + newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertySetService, "属性设置服务")); } // 2.3 生成属性获取服务 ThingModelService propertyGetService = generatePropertyGetService(propertyList); if (propertyGetService != null) { - newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertyGetService)); + newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertyGetService,"属性获取服务")); } // 3.1 获取数据库中的默认的旧事件和服务列表 @@ -246,18 +246,20 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 构建事件功能对象 */ - private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event) { + private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event, + String description) { return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) - .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(event.getDescription()) + .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description) .setType(IotProductThingModelTypeEnum.EVENT.getType()).setEvent(event); } /** * 构建服务功能对象 */ - private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service) { + private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service, + String description) { return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) - .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(service.getDescription()) + .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description) .setType(IotProductThingModelTypeEnum.SERVICE.getType()).setService(service); } @@ -271,8 +273,8 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } // 1.2 生成属性上报事件 - return new ThingModelEvent().setIdentifier("post").setName("属性上报").setDescription("属性上报事件") - .setType(IotProductThingModelServiceEventTypeEnum.INFO.getType()).setMethod("thing.event.property.post") + return new ThingModelEvent().setIdentifier("post").setName("属性上报").setMethod("thing.event.property.post") + .setType(IotProductThingModelServiceEventTypeEnum.INFO.getType()) .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); } @@ -289,8 +291,8 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } // 2. 生成属性设置服务 - return new ThingModelService().setIdentifier("set").setName("属性设置").setDescription("属性设置服务") - .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()).setMethod("thing.service.property.set") + return new ThingModelService().setIdentifier("set").setName("属性设置").setMethod("thing.service.property.set") + .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()) .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数 } @@ -305,8 +307,8 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } // 1.2 生成属性获取服务 - return new ThingModelService().setIdentifier("get").setName("属性获取").setDescription("属性获取服务") - .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()).setMethod("thing.service.property.get") + return new ThingModelService().setIdentifier("get").setName("属性获取").setMethod("thing.service.property.get") + .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()) .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); } From b4288bc39397f9de68d3f3ada18bc7fe5435ab5e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 26 Dec 2024 13:56:38 +0800 Subject: [PATCH 058/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AThingModel=20=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingmodel/model/ThingModelEvent.java | 7 +- .../thingmodel/model/ThingModelService.java | 7 +- .../IotProductThingModelServiceImpl.java | 73 ++++++++++--------- 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index f49f5f86e6..f6b45deb5f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -22,16 +22,13 @@ public class ThingModelEvent { */ private String name; /** - * 是否是标准品类的必选事件。 - * - * - true:是 - * - false:否 + * 是否是标准品类的必选事件 */ private Boolean required; /** * 事件类型 * - * 关联枚举 {@link IotProductThingModelServiceEventTypeEnum} + * 枚举 {@link IotProductThingModelServiceEventTypeEnum} */ private String type; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 0bdf498d3c..95a7029995 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -22,16 +22,13 @@ public class ThingModelService { */ private String name; /** - * 是否是标准品类的必选服务。 - * - * - true:是 - * - false:否 + * 是否是标准品类的必选服务 */ private Boolean required; /** * 调用类型 * - * 关联枚举 {@link IotProductThingModelServiceCallTypeEnum} + * 枚举 {@link IotProductThingModelServiceCallTypeEnum} */ private String callType; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java index 3dc5790a5e..9e20a710b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java @@ -185,43 +185,44 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 - List propertyList = productThingModelMapper + List properties = productThingModelMapper .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 - List newThingModelList = new ArrayList<>(); + List newThingModels = new ArrayList<>(); // 2.1 生成属性上报事件 - ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); + ThingModelEvent propertyPostEvent = generatePropertyPostEvent(properties); if (propertyPostEvent != null) { - newThingModelList.add(buildEventThingModelDO(productId, productKey, propertyPostEvent, "属性上报事件")); + newThingModels.add(buildEventThingModelDO(productId, productKey, propertyPostEvent, "属性上报事件")); } // 2.2 生成属性设置服务 - ThingModelService propertySetService = generatePropertySetService(propertyList); + ThingModelService propertySetService = generatePropertySetService(properties); if (propertySetService != null) { - newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertySetService, "属性设置服务")); + newThingModels.add(buildServiceThingModelDO(productId, productKey, propertySetService, "属性设置服务")); } // 2.3 生成属性获取服务 - ThingModelService propertyGetService = generatePropertyGetService(propertyList); + ThingModelService propertyGetService = generatePropertyGetService(properties); if (propertyGetService != null) { - newThingModelList.add(buildServiceThingModelDO(productId, productKey, propertyGetService,"属性获取服务")); + newThingModels.add(buildServiceThingModelDO(productId, productKey, propertyGetService,"属性获取服务")); } // 3.1 获取数据库中的默认的旧事件和服务列表 - List oldThingModelList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( + List oldThingModels = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) ); // 3.2 创建默认的事件和服务 - createDefaultEventsAndServices(oldThingModelList, newThingModelList); + createDefaultEventsAndServices(oldThingModels, newThingModels); } /** * 创建默认的事件和服务 */ - private void createDefaultEventsAndServices(List oldThingModelList, List newThingModelList) { - // 1.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldThingModelList, newThingModelList, + private void createDefaultEventsAndServices(List oldThingModels, + List newThingModels) { + // 使用 diffList 方法比较新旧列表 + List> diffResult = diffList(oldThingModels, newThingModels, (oldVal, newVal) -> { // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 boolean same = Objects.equals(oldVal.getIdentifier(), newVal.getIdentifier()) @@ -231,7 +232,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } return same; }); - // 1.2 批量添加、修改、删除 + // 批量添加、修改、删除 if (CollUtil.isNotEmpty(diffResult.get(0))) { productThingModelMapper.insertBatch(diffResult.get(0)); } @@ -246,8 +247,8 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 构建事件功能对象 */ - private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event, - String description) { + private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, + ThingModelEvent event, String description) { return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description) .setType(IotProductThingModelTypeEnum.EVENT.getType()).setEvent(event); @@ -256,8 +257,8 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 构建服务功能对象 */ - private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service, - String description) { + private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, + ThingModelService service, String description) { return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description) .setType(IotProductThingModelTypeEnum.SERVICE.getType()).setService(service); @@ -266,64 +267,64 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 生成属性上报事件 */ - private ThingModelEvent generatePropertyPostEvent(List thingModelList) { - // 1.1 没有属性则不生成 - if (CollUtil.isEmpty(thingModelList)) { + private ThingModelEvent generatePropertyPostEvent(List thingModels) { + // 没有属性则不生成 + if (CollUtil.isEmpty(thingModels)) { return null; } - // 1.2 生成属性上报事件 + // 生成属性上报事件 return new ThingModelEvent().setIdentifier("post").setName("属性上报").setMethod("thing.event.property.post") .setType(IotProductThingModelServiceEventTypeEnum.INFO.getType()) - .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); + .setOutputParams(buildInputOutputParam(thingModels, IotProductThingModelParamDirectionEnum.OUTPUT)); } /** * 生成属性设置服务 */ - private ThingModelService generatePropertySetService(List thingModelList) { + private ThingModelService generatePropertySetService(List thingModels) { // 1.1 过滤出所有可写属性 - thingModelList = filterList(thingModelList, thingModel -> + thingModels = filterList(thingModels, thingModel -> IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode())); // 1.2 没有可写属性则不生成 - if (CollUtil.isEmpty(thingModelList)) { + if (CollUtil.isEmpty(thingModels)) { return null; } // 2. 生成属性设置服务 return new ThingModelService().setIdentifier("set").setName("属性设置").setMethod("thing.service.property.set") .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()) - .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) + .setInputParams(buildInputOutputParam(thingModels, IotProductThingModelParamDirectionEnum.INPUT)) .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数 } /** * 生成属性获取服务 */ - private ThingModelService generatePropertyGetService(List thingModelList) { + private ThingModelService generatePropertyGetService(List thingModels) { // 1.1 没有属性则不生成 - if (CollUtil.isEmpty(thingModelList)) { + if (CollUtil.isEmpty(thingModels)) { return null; } // 1.2 生成属性获取服务 return new ThingModelService().setIdentifier("get").setName("属性获取").setMethod("thing.service.property.get") .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()) - .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) - .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); + .setInputParams(buildInputOutputParam(thingModels, IotProductThingModelParamDirectionEnum.INPUT)) + .setOutputParams(buildInputOutputParam(thingModels, IotProductThingModelParamDirectionEnum.OUTPUT)); } /** * 构建输入/输出参数列表 * - * @param thingModelList 属性列表 + * @param thingModels 属性列表 * @return 输入/输出参数列表 */ - private List buildInputOutputParam(List thingModelList, - IotProductThingModelParamDirectionEnum directionEnum) { - return convertList(thingModelList, thingModel -> + private List buildInputOutputParam(List thingModels, + IotProductThingModelParamDirectionEnum direction) { + return convertList(thingModels, thingModel -> BeanUtils.toBean(thingModel.getProperty(), ThingModelInputOutputParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事 - .setDirection(directionEnum.getDirection())); + .setDirection(direction.getDirection())); } } From 8b7f329183e21b365222401c8a2c7d32108f0ee5 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 27 Dec 2024 10:37:16 +0800 Subject: [PATCH 059/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IOT:=20ThingModel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ....java => IotThingModelAccessModeEnum.java} | 2 +- ...a => IotThingModelParamDirectionEnum.java} | 2 +- ... => IotThingModelServiceCallTypeEnum.java} | 2 +- ...=> IotThingModelServiceEventTypeEnum.java} | 2 +- ...peEnum.java => IotThingModelTypeEnum.java} | 8 +- .../IotProductThingModelController.java | 84 ------------ ...ller.http => IotThingModelController.http} | 10 +- .../thingmodel/IotThingModelController.java | 84 ++++++++++++ .../thingmodel/model/ThingModelEvent.java | 6 +- ...tOutputParam.java => ThingModelParam.java} | 8 +- .../thingmodel/model/ThingModelProperty.java | 4 +- .../thingmodel/model/ThingModelService.java | 8 +- .../dataType/ThingModelStructDataSpecs.java | 4 +- ...ReqVO.java => IotThingModelPageReqVO.java} | 6 +- ...elRespVO.java => IotThingModelRespVO.java} | 2 +- ...ReqVO.java => IotThingModelSaveReqVO.java} | 6 +- ...Convert.java => IotThingModelConvert.java} | 30 ++-- .../dataobject/device/IotDeviceDataDO.java | 10 +- ...ThingModelDO.java => IotThingModelDO.java} | 13 +- .../IotProductThingModelMapper.java | 62 --------- .../mysql/thingmodel/IotThingModelMapper.java | 62 +++++++++ .../IotDevicePropertyDataServiceImpl.java | 18 +-- .../IotThingModelMessageServiceImpl.java | 40 +++--- ...Service.java => IotThingModelService.java} | 22 +-- ...mpl.java => IotThingModelServiceImpl.java} | 128 +++++++++--------- 25 files changed, 308 insertions(+), 315 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/{IotProductThingModelAccessModeEnum.java => IotThingModelAccessModeEnum.java} (85%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/{IotProductThingModelParamDirectionEnum.java => IotThingModelParamDirectionEnum.java} (86%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/{IotProductThingModelServiceCallTypeEnum.java => IotThingModelServiceCallTypeEnum.java} (85%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/{IotProductThingModelServiceEventTypeEnum.java => IotThingModelServiceEventTypeEnum.java} (85%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/{IotProductThingModelTypeEnum.java => IotThingModelTypeEnum.java} (74%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/{IotProductThingModelController.http => IotThingModelController.http} (94%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/{ThingModelInputOutputParam.java => ThingModelParam.java} (80%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/{IotProductThingModelPageReqVO.java => IotThingModelPageReqVO.java} (82%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/{IotProductThingModelRespVO.java => IotThingModelRespVO.java} (98%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/{IotProductThingModelSaveReqVO.java => IotThingModelSaveReqVO.java} (92%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/{IotProductThingModelConvert.java => IotThingModelConvert.java} (58%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/{IotProductThingModelDO.java => IotThingModelDO.java} (75%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/{IotProductThingModelService.java => IotThingModelService.java} (64%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/{IotProductThingModelServiceImpl.java => IotThingModelServiceImpl.java} (65%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java index 7fca10a467..0b18bbbf5a 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java @@ -10,7 +10,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotProductThingModelAccessModeEnum { +public enum IotThingModelAccessModeEnum { READ_ONLY("r"), READ_WRITE("rw"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java index e12332b6dd..b1ae8a9471 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelParamDirectionEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java @@ -10,7 +10,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotProductThingModelParamDirectionEnum { +public enum IotThingModelParamDirectionEnum { INPUT("input"), // 输入参数 OUTPUT("output"); // 输出参数 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java index 81ffccc493..66ca6bec57 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceCallTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java @@ -10,7 +10,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotProductThingModelServiceCallTypeEnum { +public enum IotThingModelServiceCallTypeEnum { ASYNC("async"), // 异步调用 SYNC("sync"); // 同步调用 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java similarity index 85% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java index fbf76da77b..b87d7eb982 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelServiceEventTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java @@ -10,7 +10,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotProductThingModelServiceEventTypeEnum { +public enum IotThingModelServiceEventTypeEnum { INFO("info"), // 信息 ALERT("alert"), // 告警 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java similarity index 74% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java index 153e93ecc8..93fdc0ca48 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotProductThingModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java @@ -13,13 +13,13 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum IotProductThingModelTypeEnum implements IntArrayValuable { +public enum IotThingModelTypeEnum implements IntArrayValuable { PROPERTY(1, "属性"), SERVICE(2, "服务"), EVENT(3, "事件"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotProductThingModelTypeEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotThingModelTypeEnum::getType).toArray(); /** * 类型 @@ -30,8 +30,8 @@ public enum IotProductThingModelTypeEnum implements IntArrayValuable { */ private final String description; - public static IotProductThingModelTypeEnum valueOfType(Integer type) { - for (IotProductThingModelTypeEnum value : values()) { + public static IotThingModelTypeEnum valueOfType(Integer type) { + for (IotThingModelTypeEnum value : values()) { if (value.getType().equals(type)) { return value; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java deleted file mode 100644 index 9b94def428..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 产品物模型") -@RestController -@RequestMapping("/iot/product-thing-model") -@Validated -public class IotProductThingModelController { - - @Resource - private IotProductThingModelService thingModelService; - - @PostMapping("/create") - @Operation(summary = "创建产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:create')") - public CommonResult createProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO createReqVO) { - return success(thingModelService.createProductThingModel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新产品物模型") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:update')") - public CommonResult updateProductThingModel(@Valid @RequestBody IotProductThingModelSaveReqVO updateReqVO) { - thingModelService.updateProductThingModel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:delete')") - public CommonResult deleteProductThingModel(@RequestParam("id") Long id) { - thingModelService.deleteProductThingModel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得产品物模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult getProductThingModel(@RequestParam("id") Long id) { - IotProductThingModelDO thingModel = thingModelService.getProductThingModel(id); - return success(IotProductThingModelConvert.INSTANCE.convert(thingModel)); - } - - @GetMapping("/list-by-product-id") - @Operation(summary = "获得产品物模型") - @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult> getProductThingModelListByProductId(@RequestParam("productId") Long productId) { - List list = thingModelService.getProductThingModelListByProductId(productId); - return success(IotProductThingModelConvert.INSTANCE.convertList(list)); - } - - @GetMapping("/page") - @Operation(summary = "获得产品物模型分页") - @PreAuthorize("@ss.hasPermission('iot:product-thing-model:query')") - public CommonResult> getProductThingModelPage(@Valid IotProductThingModelPageReqVO pageReqVO) { - PageResult pageResult = thingModelService.getProductThingModelPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotProductThingModelRespVO.class)); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http index 7e7aa05d6e..1e1f72103e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotProductThingModelController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.http @@ -25,8 +25,7 @@ Authorization: Bearer {{token}} "defaultValue": "30", "unit": "%", "unitName": "百分比" - }, - "description": "当前温度值" + } } } @@ -46,7 +45,6 @@ Authorization: Bearer {{token}} "property": { "identifier": "switch", "name": "开关", - "description": "温度计开关", "accessMode": "rw", "required": true, "dataType": "bool", @@ -81,7 +79,6 @@ Authorization: Bearer {{token}} "property": { "identifier": "argb", "name": "温度计 argb 颜色", - "description": "温度计 argb 颜色", "accessMode": "rw", "required": true, "dataType": "array", @@ -93,7 +90,6 @@ Authorization: Bearer {{token}} { "identifier": "switch", "name": "开关", - "description": "温度计开关", "accessMode": "rw", "required": true, "dataType": "struct", @@ -126,8 +122,7 @@ Authorization: Bearer {{token}} "defaultValue": "30", "unit": "%", "unitName": "百分比" - }, - "description": "当前温度值" + } } ] } @@ -151,7 +146,6 @@ Authorization: Bearer {{token}} "property": { "identifier": "switch", "name": "开关", - "description": "温度计开关", "accessMode": "r", "required": true, "dataType": "bool", diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java new file mode 100644 index 0000000000..4397a742f4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.iot.controller.admin.thingmodel; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 产品物模型") +@RestController +@RequestMapping("/iot/thing-model") +@Validated +public class IotThingModelController { + + @Resource + private IotThingModelService thingModelService; + + @PostMapping("/create") + @Operation(summary = "创建产品物模型") + @PreAuthorize("@ss.hasPermission('iot:thing-model:create')") + public CommonResult createThingModel(@Valid @RequestBody IotThingModelSaveReqVO createReqVO) { + return success(thingModelService.createThingModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品物模型") + @PreAuthorize("@ss.hasPermission('iot:thing-model:update')") + public CommonResult updateThingModel(@Valid @RequestBody IotThingModelSaveReqVO updateReqVO) { + thingModelService.updateThingModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:thing-model:delete')") + public CommonResult deleteThingModel(@RequestParam("id") Long id) { + thingModelService.deleteThingModel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品物模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") + public CommonResult getThingModel(@RequestParam("id") Long id) { + IotThingModelDO thingModel = thingModelService.getThingModel(id); + return success(IotThingModelConvert.INSTANCE.convert(thingModel)); + } + + @GetMapping("/list-by-product-id") + @Operation(summary = "获得产品物模型") + @Parameter(name = "productId", description = "产品ID", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") + public CommonResult> getThingModelListByProductId(@RequestParam("productId") Long productId) { + List list = thingModelService.getThingModelListByProductId(productId); + return success(IotThingModelConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品物模型分页") + @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") + public CommonResult> getThingModelPage(@Valid IotThingModelPageReqVO pageReqVO) { + PageResult pageResult = thingModelService.getProductThingModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotThingModelRespVO.class)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index f49f5f86e6..286251aae5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelServiceEventTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum; import lombok.Data; import java.util.List; @@ -31,7 +31,7 @@ public class ThingModelEvent { /** * 事件类型 * - * 关联枚举 {@link IotProductThingModelServiceEventTypeEnum} + * 关联枚举 {@link IotThingModelServiceEventTypeEnum} */ private String type; /** @@ -39,7 +39,7 @@ public class ThingModelEvent { * * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 */ - private List outputParams; + private List outputParams; /** * 标识设备需要执行的具体操作 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java similarity index 80% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java index 3b7b1414fa..893fc1d157 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelInputOutputParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -1,18 +1,18 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelParamDirectionEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum; import lombok.Data; import java.util.List; /** - * IOT 产品物模型中的参数 // TODO @puhui999 考虑要不改成 ThingModelParam ? + * IOT 产品物模型中的参数 * * @author HUIHUI */ @Data -public class ThingModelInputOutputParam { +public class ThingModelParam { /** * 参数标识符 @@ -25,7 +25,7 @@ public class ThingModelInputOutputParam { /** * 用于区分输入或输出参数 * - * 关联枚举 {@link IotProductThingModelParamDirectionEnum} + * 关联枚举 {@link IotThingModelParamDirectionEnum} */ private String direction; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index bb7376fc8c..db7333df25 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; import lombok.Data; import java.util.List; @@ -27,7 +27,7 @@ public class ThingModelProperty { /** * 云端可以对该属性进行的操作类型 * - * 枚举 {@link IotProductThingModelAccessModeEnum} + * 枚举 {@link IotThingModelAccessModeEnum} */ private String accessMode; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 0bdf498d3c..619b64d053 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelServiceCallTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum; import lombok.Data; import java.util.List; @@ -31,7 +31,7 @@ public class ThingModelService { /** * 调用类型 * - * 关联枚举 {@link IotProductThingModelServiceCallTypeEnum} + * 关联枚举 {@link IotThingModelServiceCallTypeEnum} */ private String callType; /** @@ -39,13 +39,13 @@ public class ThingModelService { * * 输入参数定义服务调用时所需提供的信息,用于控制设备行为或执行特定任务 */ - private List inputParams; + private List inputParams; /** * 服务的输出参数 * * 输出参数定义服务调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 */ - private List outputParams; + private List outputParams; /** * 标识设备需要执行的具体操作 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java index 5e31d3996a..43addca957 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelAccessModeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; @@ -27,7 +27,7 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { private String name; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotProductThingModelAccessModeEnum} + * 关联枚举 {@link IotThingModelAccessModeEnum} */ private String accessMode; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java index a15ae98a1c..314441fb62 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; @@ -13,7 +13,7 @@ import lombok.ToString; @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) -public class IotProductThingModelPageReqVO extends PageParam { +public class IotThingModelPageReqVO extends PageParam { @Schema(description = "功能标识") private String identifier; @@ -22,7 +22,7 @@ public class IotProductThingModelPageReqVO extends PageParam { private String name; @Schema(description = "功能类型", example = "1") - @InEnum(IotProductThingModelTypeEnum.class) + @InEnum(IotThingModelTypeEnum.class) private Integer type; @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java similarity index 98% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java index 8d9c370d99..88c197babb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated -public class IotProductThingModelRespVO { +public class IotThingModelRespVO { @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") @ExcelProperty("产品ID") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java similarity index 92% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java index 5f2be48478..0eb37231bd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotProductThingModelSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -12,7 +12,7 @@ import lombok.Data; @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data -public class IotProductThingModelSaveReqVO { +public class IotThingModelSaveReqVO { @Schema(description = "编号", example = "1") private Long id; @@ -38,7 +38,7 @@ public class IotProductThingModelSaveReqVO { @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "功能类型不能为空") - @InEnum(IotProductThingModelTypeEnum.class) + @InEnum(IotThingModelTypeEnum.class) private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java similarity index 58% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java index cb10c62b5d..2e9b5441ce 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotProductThingModelConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.convert.thingmodel; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; @@ -16,44 +16,44 @@ import java.util.List; import java.util.Objects; @Mapper -public interface IotProductThingModelConvert { +public interface IotThingModelConvert { - IotProductThingModelConvert INSTANCE = Mappers.getMapper(IotProductThingModelConvert.class); + IotThingModelConvert INSTANCE = Mappers.getMapper(IotThingModelConvert.class); // 将 SaveReqVO 转换为 DO @Mapping(target = "property", expression = "java(convertToProperty(bean))") @Mapping(target = "event", expression = "java(convertToEvent(bean))") @Mapping(target = "service", expression = "java(convertToService(bean))") - IotProductThingModelDO convert(IotProductThingModelSaveReqVO bean); + IotThingModelDO convert(IotThingModelSaveReqVO bean); // 将 DO 转换为 RespVO @Mapping(target = "property", source = "property") @Mapping(target = "event", source = "event") @Mapping(target = "service", source = "service") - IotProductThingModelRespVO convert(IotProductThingModelDO bean); + IotThingModelRespVO convert(IotThingModelDO bean); // 批量转换 - List convertList(List list); + List convertList(List list); @Named("convertToProperty") - default ThingModelProperty convertToProperty(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + default ThingModelProperty convertToProperty(IotThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { return bean.getProperty(); } return null; } @Named("convertToEvent") - default ThingModelEvent convertToEvent(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.EVENT.getType())) { + default ThingModelEvent convertToEvent(IotThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.EVENT.getType())) { return bean.getEvent(); } return null; } @Named("convertToService") - default ThingModelService convertToService(IotProductThingModelSaveReqVO bean) { - if (Objects.equals(bean.getType(), IotProductThingModelTypeEnum.SERVICE.getType())) { + default ThingModelService convertToService(IotThingModelSaveReqVO bean) { + if (Objects.equals(bean.getType(), IotThingModelTypeEnum.SERVICE.getType())) { return bean.getService(); } return null; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java index ab1934edbf..63b732d20f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -30,7 +30,7 @@ public class IotDeviceDataDO { /** * 物模型编号 *

- * 关联 {@link IotProductThingModelDO#getId()} + * 关联 {@link IotThingModelDO#getId()} */ private Long thingModelId; @@ -51,21 +51,21 @@ public class IotDeviceDataDO { /** * 属性标识符 *

- * 关联 {@link IotProductThingModelDO#getIdentifier()} + * 关联 {@link IotThingModelDO#getIdentifier()} */ private String identifier; /** * 属性名称 *

- * 关联 {@link IotProductThingModelDO#getName()} + * 关联 {@link IotThingModelDO#getName()} */ private String name; /** * 数据类型 *

- * 关联 {@link IotProductThingModelDO#getProperty()#getDataType()} + * 关联 {@link IotThingModelDO#getProperty()#getDataType()} */ private String dataType; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java similarity index 75% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java index f2edc5db7f..e3b4a6d9ae 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotProductThingModelDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelE import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -16,21 +16,20 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -// TODO @huihui:IotProductThingModelDO => IotThingModelDO /** * IoT 产品物模型功能 DO *

- * 每个 {@link IotProductDO} 和 {@link IotProductThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 + * 每个 {@link IotProductDO} 和 {@link IotThingModelDO} 是“一对多”的关系,它的每个属性、事件、服务都对应一条记录 * * @author 芋道源码 */ -@TableName(value = "iot_product_thing_model", autoResultMap = true) -@KeySequence("iot_product_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName(value = "iot_thing_model", autoResultMap = true) +@KeySequence("iot_thing_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class IotProductThingModelDO extends BaseDO { +public class IotThingModelDO extends BaseDO { /** * 物模型功能编号 @@ -67,7 +66,7 @@ public class IotProductThingModelDO extends BaseDO { /** * 功能类型 *

- * 枚举 {@link IotProductThingModelTypeEnum} + * 枚举 {@link IotThingModelTypeEnum} */ private Integer type; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java deleted file mode 100644 index 175f66f402..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotProductThingModelMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * IoT 产品物模型 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface IotProductThingModelMapper extends BaseMapperX { - - default PageResult selectPage(IotProductThingModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(IotProductThingModelDO::getIdentifier, reqVO.getIdentifier()) - .likeIfPresent(IotProductThingModelDO::getName, reqVO.getName()) - .eqIfPresent(IotProductThingModelDO::getType, reqVO.getType()) - .eqIfPresent(IotProductThingModelDO::getProductId, reqVO.getProductId()) - .notIn(IotProductThingModelDO::getIdentifier, "get", "set", "post") - .orderByDesc(IotProductThingModelDO::getId)); - } - - default IotProductThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { - return selectOne(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getIdentifier, identifier); - } - - default List selectListByProductId(Long productId) { - return selectList(IotProductThingModelDO::getProductId, productId); - } - - default List selectListByProductIdAndType(Long productId, Integer type) { - return selectList(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getType, type); - } - - default List selectListByProductIdAndIdentifiersAndTypes(Long productId, - List identifiers, - List types) { - return selectList(new LambdaQueryWrapperX() - .eq(IotProductThingModelDO::getProductId, productId) - .in(IotProductThingModelDO::getIdentifier, identifiers) - .in(IotProductThingModelDO::getType, types)); - } - - default IotProductThingModelDO selectByProductIdAndName(Long productId, String name) { - return selectOne(IotProductThingModelDO::getProductId, productId, - IotProductThingModelDO::getName, name); - } - - default List selectListByProductKey(String productKey) { - return selectList(IotProductThingModelDO::getProductKey, productKey); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java new file mode 100644 index 0000000000..b52abad8bd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.thingmodel; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IoT 产品物模型 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotThingModelMapper extends BaseMapperX { + + default PageResult selectPage(IotThingModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotThingModelDO::getIdentifier, reqVO.getIdentifier()) + .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) + .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) + .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) + .notIn(IotThingModelDO::getIdentifier, "get", "set", "post") + .orderByDesc(IotThingModelDO::getId)); + } + + default IotThingModelDO selectByProductIdAndIdentifier(Long productId, String identifier) { + return selectOne(IotThingModelDO::getProductId, productId, + IotThingModelDO::getIdentifier, identifier); + } + + default List selectListByProductId(Long productId) { + return selectList(IotThingModelDO::getProductId, productId); + } + + default List selectListByProductIdAndType(Long productId, Integer type) { + return selectList(IotThingModelDO::getProductId, productId, + IotThingModelDO::getType, type); + } + + default List selectListByProductIdAndIdentifiersAndTypes(Long productId, + List identifiers, + List types) { + return selectList(new LambdaQueryWrapperX() + .eq(IotThingModelDO::getProductId, productId) + .in(IotThingModelDO::getIdentifier, identifiers) + .in(IotThingModelDO::getType, types)); + } + + default IotThingModelDO selectByProductIdAndName(Long productId, String name) { + return selectOne(IotThingModelDO::getProductId, productId, + IotThingModelDO::getName, name); + } + + default List selectListByProductKey(String productKey) { + return selectList(IotThingModelDO::getProductKey, productKey); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 99c04a843b..eb7fcd430c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -13,17 +13,17 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; @@ -71,7 +71,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe @Resource private IotThingModelMessageService thingModelMessageService; @Resource - private IotProductThingModelService thingModelService; + private IotThingModelService thingModelService; @Resource private IotProductService productService; @@ -88,8 +88,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe public void defineDevicePropertyData(Long productId) { // 1.1 查询产品和物模型 IotProductDO product = productService.validateProductExists(productId); - List thingModels = filterList(thingModelService.getProductThingModelListByProductId(productId), - thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + List thingModels = filterList(thingModelService.getThingModelListByProductId(productId), + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); // 1.2 解析 DB 里的字段 List oldFields = new ArrayList<>(); try { @@ -115,7 +115,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); } - private List buildTableFieldList(List thingModels) { + private List buildTableFieldList(List thingModels) { return convertList(thingModels, thingModel -> { TDengineTableField field = new TDengineTableField( StrUtil.toUnderlineCase(thingModel.getIdentifier()), // TDengine 字段默认都是小写 @@ -153,8 +153,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe // 1. 获取设备信息 IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); // 2. 获取设备属性最新数据 - List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); - thingModelList = filterList(thingModelList, thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType() + List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); + thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType() .equals(thingModel.getType())); // 3. 过滤标识符和属性名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index ad909c437f..d910419956 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -11,16 +11,16 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotProductThingModelTypeEnum; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotProductThingModelService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; import jakarta.annotation.Resource; @@ -52,7 +52,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ private String url; @Resource - private IotProductThingModelService iotProductThingModelService; + private IotThingModelService iotThingModelService; @Resource private IotDeviceService iotDeviceService; @Resource @@ -83,7 +83,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 Map params = thingModelMessage.dataToMap(); - List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); + List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); if (thingModelList.isEmpty()) { return; } @@ -102,9 +102,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ .build()); } - private List getValidThingModelList(String productKey) { - return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + private List getValidThingModelList(String productKey) { + return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } @Override @@ -134,17 +134,17 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); } - private List getValidFunctionList(String productKey) { - return filterList(iotProductThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotProductThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); + private List getValidFunctionList(String productKey) { + return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), + thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } - private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { + private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { // 1. 获取属性标识符集合 - Set propertyIdentifiers = convertSet(thingModelList, IotProductThingModelDO::getIdentifier); + Set propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier); // 2. 构建属性标识符和属性的映射 - Map thingModelMap = convertMap(thingModelList, IotProductThingModelDO::getIdentifier); + Map thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier); // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); @@ -164,21 +164,21 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * 缓存设备属性 * * @param device 设备信息 - * @param iotProductThingModelDO 物模型属性 + * @param iotThingModelDO 物模型属性 * @param val 属性值 * @param time 时间 */ - private void setDeviceDataCache(IotDeviceDO device, IotProductThingModelDO iotProductThingModelDO, Object val, Long time) { + private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) { IotDeviceDataDO deviceData = IotDeviceDataDO.builder() .productKey(device.getProductKey()) .deviceName(device.getDeviceName()) - .identifier(iotProductThingModelDO.getIdentifier()) + .identifier(iotThingModelDO.getIdentifier()) .value(val != null ? val.toString() : null) .updateTime(DateUtil.toLocalDateTime(new Date(time))) .deviceId(device.getId()) - .thingModelId(iotProductThingModelDO.getId()) - .name(iotProductThingModelDO.getName()) - .dataType(iotProductThingModelDO.getProperty().getDataType()) + .thingModelId(iotThingModelDO.getId()) + .name(iotThingModelDO.getName()) + .dataType(iotThingModelDO.getProperty().getDataType()) .build(); deviceDataRedisDAO.set(deviceData); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java similarity index 64% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index 6df29e1018..f2ec53ecfc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import jakarta.validation.Valid; import java.util.List; @@ -13,7 +13,7 @@ import java.util.List; * * @author 芋道源码 */ -public interface IotProductThingModelService { +public interface IotThingModelService { /** * 创建产品物模型 @@ -21,21 +21,21 @@ public interface IotProductThingModelService { * @param createReqVO 创建信息 * @return 编号 */ - Long createProductThingModel(@Valid IotProductThingModelSaveReqVO createReqVO); + Long createThingModel(@Valid IotThingModelSaveReqVO createReqVO); /** * 更新产品物模型 * * @param updateReqVO 更新信息 */ - void updateProductThingModel(@Valid IotProductThingModelSaveReqVO updateReqVO); + void updateThingModel(@Valid IotThingModelSaveReqVO updateReqVO); /** * 删除产品物模型 * * @param id 编号 */ - void deleteProductThingModel(Long id); + void deleteThingModel(Long id); /** * 获得产品物模型 @@ -43,7 +43,7 @@ public interface IotProductThingModelService { * @param id 编号 * @return 产品物模型 */ - IotProductThingModelDO getProductThingModel(Long id); + IotThingModelDO getThingModel(Long id); /** * 获得产品物模型列表 @@ -51,7 +51,7 @@ public interface IotProductThingModelService { * @param productId 产品编号 * @return 产品物模型列表 */ - List getProductThingModelListByProductId(Long productId); + List getThingModelListByProductId(Long productId); /** * 获得产品物模型分页 @@ -59,7 +59,7 @@ public interface IotProductThingModelService { * @param pageReqVO 分页查询 * @return 产品物模型分页 */ - PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO); + PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO); /** * 获得产品物模型列表 @@ -67,6 +67,6 @@ public interface IotProductThingModelService { * @param productKey 产品 Key * @return 产品物模型列表 */ - List getProductThingModelListByProductKey(String productKey); + List getProductThingModelListByProductKey(String productKey); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java similarity index 65% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index 3dc5790a5e..4617bd15b2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -5,14 +5,14 @@ import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelInputOutputParam; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotProductThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotProductThingModelConvert; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotProductThingModelDO; -import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotProductThingModelMapper; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.*; import cn.iocoder.yudao.module.iot.service.product.IotProductService; @@ -36,17 +36,17 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @Service @Validated @Slf4j -public class IotProductThingModelServiceImpl implements IotProductThingModelService { +public class IotThingModelServiceImpl implements IotThingModelService { @Resource - private IotProductThingModelMapper productThingModelMapper; + private IotThingModelMapper thingModelMapper; @Resource private IotProductService productService; @Override @Transactional(rollbackFor = Exception.class) - public Long createProductThingModel(IotProductThingModelSaveReqVO createReqVO) { + public Long createThingModel(IotThingModelSaveReqVO createReqVO) { // 1. 校验功能标识符在同一产品下是否唯一 validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); @@ -60,11 +60,11 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ validateProductStatus(createReqVO.getProductId()); // 5. 插入数据库 - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(createReqVO); - productThingModelMapper.insert(thingModel); + IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO); + thingModelMapper.insert(thingModel); // 6. 如果创建的是属性,需要更新默认的事件和服务 - if (Objects.equals(createReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(createReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? @@ -73,7 +73,7 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ @Override @Transactional(rollbackFor = Exception.class) - public void updateProductThingModel(IotProductThingModelSaveReqVO updateReqVO) { + public void updateThingModel(IotThingModelSaveReqVO updateReqVO) { // 1. 校验功能是否存在 validateProductThingModelMapperExists(updateReqVO.getId()); @@ -84,20 +84,20 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ validateProductStatus(updateReqVO.getProductId()); // 4. 更新数据库 - IotProductThingModelDO thingModel = IotProductThingModelConvert.INSTANCE.convert(updateReqVO); - productThingModelMapper.updateById(thingModel); + IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); + thingModelMapper.updateById(thingModel); // 5. 如果更新的是属性,需要更新默认的事件和服务 - if (Objects.equals(updateReqVO.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } } @Override @Transactional(rollbackFor = Exception.class) - public void deleteProductThingModel(Long id) { + public void deleteThingModel(Long id) { // 1. 校验功能是否存在 - IotProductThingModelDO thingModel = productThingModelMapper.selectById(id); + IotThingModelDO thingModel = thingModelMapper.selectById(id); if (thingModel == null) { throw exception(THING_MODEL_NOT_EXISTS); } @@ -106,32 +106,32 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ validateProductStatus(thingModel.getProductId()); // 2. 删除功能 - productThingModelMapper.deleteById(id); + thingModelMapper.deleteById(id); // 3. 如果删除的是属性,需要更新默认的事件和服务 - if (Objects.equals(thingModel.getType(), IotProductThingModelTypeEnum.PROPERTY.getType())) { + if (Objects.equals(thingModel.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); } } @Override - public IotProductThingModelDO getProductThingModel(Long id) { - return productThingModelMapper.selectById(id); + public IotThingModelDO getThingModel(Long id) { + return thingModelMapper.selectById(id); } @Override - public List getProductThingModelListByProductId(Long productId) { - return productThingModelMapper.selectListByProductId(productId); + public List getThingModelListByProductId(Long productId) { + return thingModelMapper.selectListByProductId(productId); } @Override - public PageResult getProductThingModelPage(IotProductThingModelPageReqVO pageReqVO) { - return productThingModelMapper.selectPage(pageReqVO); + public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { + return thingModelMapper.selectPage(pageReqVO); } @Override - public List getProductThingModelListByProductKey(String productKey) { - return productThingModelMapper.selectListByProductKey(productKey); + public List getProductThingModelListByProductKey(String productKey) { + return thingModelMapper.selectListByProductKey(productKey); } /** @@ -140,13 +140,13 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ * @param id 功能编号 */ private void validateProductThingModelMapperExists(Long id) { - if (productThingModelMapper.selectById(id) == null) { + if (thingModelMapper.selectById(id) == null) { throw exception(THING_MODEL_NOT_EXISTS); } } private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { throw exception(THING_MODEL_IDENTIFIER_EXISTS); } @@ -167,14 +167,14 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } private void validateNameUnique(Long productId, String name) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndName(productId, name); + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndName(productId, name); if (thingModel != null) { throw exception(THING_MODEL_NAME_EXISTS); } } private void validateIdentifierUnique(Long productId, String identifier) { - IotProductThingModelDO thingModel = productThingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (thingModel != null) { throw exception(THING_MODEL_IDENTIFIER_EXISTS); } @@ -185,11 +185,11 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 - List propertyList = productThingModelMapper - .selectListByProductIdAndType(productId, IotProductThingModelTypeEnum.PROPERTY.getType()); + List propertyList = thingModelMapper + .selectListByProductIdAndType(productId, IotThingModelTypeEnum.PROPERTY.getType()); // 2. 生成新的事件和服务列表 - List newThingModelList = new ArrayList<>(); + List newThingModelList = new ArrayList<>(); // 2.1 生成属性上报事件 ThingModelEvent propertyPostEvent = generatePropertyPostEvent(propertyList); if (propertyPostEvent != null) { @@ -207,10 +207,10 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ } // 3.1 获取数据库中的默认的旧事件和服务列表 - List oldThingModelList = productThingModelMapper.selectListByProductIdAndIdentifiersAndTypes( + List oldThingModelList = thingModelMapper.selectListByProductIdAndIdentifiersAndTypes( productId, Arrays.asList("post", "set", "get"), - Arrays.asList(IotProductThingModelTypeEnum.EVENT.getType(), IotProductThingModelTypeEnum.SERVICE.getType()) + Arrays.asList(IotThingModelTypeEnum.EVENT.getType(), IotThingModelTypeEnum.SERVICE.getType()) ); // 3.2 创建默认的事件和服务 createDefaultEventsAndServices(oldThingModelList, newThingModelList); @@ -219,9 +219,9 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ /** * 创建默认的事件和服务 */ - private void createDefaultEventsAndServices(List oldThingModelList, List newThingModelList) { + private void createDefaultEventsAndServices(List oldThingModelList, List newThingModelList) { // 1.1 使用 diffList 方法比较新旧列表 - List> diffResult = diffList(oldThingModelList, newThingModelList, + List> diffResult = diffList(oldThingModelList, newThingModelList, (oldVal, newVal) -> { // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。 boolean same = Objects.equals(oldVal.getIdentifier(), newVal.getIdentifier()) @@ -233,40 +233,40 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ }); // 1.2 批量添加、修改、删除 if (CollUtil.isNotEmpty(diffResult.get(0))) { - productThingModelMapper.insertBatch(diffResult.get(0)); + thingModelMapper.insertBatch(diffResult.get(0)); } if (CollUtil.isNotEmpty(diffResult.get(1))) { - productThingModelMapper.updateBatch(diffResult.get(1)); + thingModelMapper.updateBatch(diffResult.get(1)); } if (CollUtil.isNotEmpty(diffResult.get(2))) { - productThingModelMapper.deleteByIds(convertSet(diffResult.get(2), IotProductThingModelDO::getId)); + thingModelMapper.deleteByIds(convertSet(diffResult.get(2), IotThingModelDO::getId)); } } /** * 构建事件功能对象 */ - private IotProductThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event, - String description) { - return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) + private IotThingModelDO buildEventThingModelDO(Long productId, String productKey, ThingModelEvent event, + String description) { + return new IotThingModelDO().setProductId(productId).setProductKey(productKey) .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description) - .setType(IotProductThingModelTypeEnum.EVENT.getType()).setEvent(event); + .setType(IotThingModelTypeEnum.EVENT.getType()).setEvent(event); } /** * 构建服务功能对象 */ - private IotProductThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service, - String description) { - return new IotProductThingModelDO().setProductId(productId).setProductKey(productKey) + private IotThingModelDO buildServiceThingModelDO(Long productId, String productKey, ThingModelService service, + String description) { + return new IotThingModelDO().setProductId(productId).setProductKey(productKey) .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description) - .setType(IotProductThingModelTypeEnum.SERVICE.getType()).setService(service); + .setType(IotThingModelTypeEnum.SERVICE.getType()).setService(service); } /** * 生成属性上报事件 */ - private ThingModelEvent generatePropertyPostEvent(List thingModelList) { + private ThingModelEvent generatePropertyPostEvent(List thingModelList) { // 1.1 没有属性则不生成 if (CollUtil.isEmpty(thingModelList)) { return null; @@ -274,17 +274,17 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ // 1.2 生成属性上报事件 return new ThingModelEvent().setIdentifier("post").setName("属性上报").setMethod("thing.event.property.post") - .setType(IotProductThingModelServiceEventTypeEnum.INFO.getType()) - .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); + .setType(IotThingModelServiceEventTypeEnum.INFO.getType()) + .setOutputParams(buildInputOutputParam(thingModelList, IotThingModelParamDirectionEnum.OUTPUT)); } /** * 生成属性设置服务 */ - private ThingModelService generatePropertySetService(List thingModelList) { + private ThingModelService generatePropertySetService(List thingModelList) { // 1.1 过滤出所有可写属性 thingModelList = filterList(thingModelList, thingModel -> - IotProductThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode())); + IotThingModelAccessModeEnum.READ_WRITE.getMode().equals(thingModel.getProperty().getAccessMode())); // 1.2 没有可写属性则不生成 if (CollUtil.isEmpty(thingModelList)) { return null; @@ -292,15 +292,15 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ // 2. 生成属性设置服务 return new ThingModelService().setIdentifier("set").setName("属性设置").setMethod("thing.service.property.set") - .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()) - .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) + .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType()) + .setInputParams(buildInputOutputParam(thingModelList, IotThingModelParamDirectionEnum.INPUT)) .setOutputParams(Collections.emptyList()); // 属性设置服务一般不需要输出参数 } /** * 生成属性获取服务 */ - private ThingModelService generatePropertyGetService(List thingModelList) { + private ThingModelService generatePropertyGetService(List thingModelList) { // 1.1 没有属性则不生成 if (CollUtil.isEmpty(thingModelList)) { return null; @@ -308,9 +308,9 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ // 1.2 生成属性获取服务 return new ThingModelService().setIdentifier("get").setName("属性获取").setMethod("thing.service.property.get") - .setCallType(IotProductThingModelServiceCallTypeEnum.ASYNC.getType()) - .setInputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.INPUT)) - .setOutputParams(buildInputOutputParam(thingModelList, IotProductThingModelParamDirectionEnum.OUTPUT)); + .setCallType(IotThingModelServiceCallTypeEnum.ASYNC.getType()) + .setInputParams(buildInputOutputParam(thingModelList, IotThingModelParamDirectionEnum.INPUT)) + .setOutputParams(buildInputOutputParam(thingModelList, IotThingModelParamDirectionEnum.OUTPUT)); } /** @@ -319,10 +319,10 @@ public class IotProductThingModelServiceImpl implements IotProductThingModelServ * @param thingModelList 属性列表 * @return 输入/输出参数列表 */ - private List buildInputOutputParam(List thingModelList, - IotProductThingModelParamDirectionEnum directionEnum) { + private List buildInputOutputParam(List thingModelList, + IotThingModelParamDirectionEnum directionEnum) { return convertList(thingModelList, thingModel -> - BeanUtils.toBean(thingModel.getProperty(), ThingModelInputOutputParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事 + BeanUtils.toBean(thingModel.getProperty(), ThingModelParam.class).setParaOrder(0) // TODO @puhui999: 先搞个默认值看看怎么个事 .setDirection(directionEnum.getDirection())); } From c9904f0994389a6b8c613e51a861745012607019 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 28 Dec 2024 21:06:19 +0800 Subject: [PATCH 060/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AThingModel=20=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crm/dal/dataobject/receivable/CrmReceivableDO.java | 4 +++- .../admin/thingmodel/model/ThingModelParam.java | 2 +- .../model/dataType/ThingModelStructDataSpecs.java | 8 +++----- .../iot/dal/mysql/thingmodel/IotThingModelMapper.java | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java index 269cac6cc2..c24614ad5d 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/receivable/CrmReceivableDO.java @@ -66,7 +66,9 @@ public class CrmReceivableDO extends BaseDO { */ private LocalDateTime returnTime; /** - * 回款方式,关联枚举{@link CrmReceivableReturnTypeEnum} + * 回款方式 + * + * 枚举 {@link CrmReceivableReturnTypeEnum} */ private Integer returnType; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java index 893fc1d157..9cddf1d290 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -25,7 +25,7 @@ public class ThingModelParam { /** * 用于区分输入或输出参数 * - * 关联枚举 {@link IotThingModelParamDirectionEnum} + * 枚举 {@link IotThingModelParamDirectionEnum} */ private String direction; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java index 43addca957..8fcaefdc5f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java @@ -27,14 +27,12 @@ public class ThingModelStructDataSpecs extends ThingModelDataSpecs { private String name; /** * 云端可以对该属性进行的操作类型 - * 关联枚举 {@link IotThingModelAccessModeEnum} + * + * 枚举 {@link IotThingModelAccessModeEnum} */ private String accessMode; /** - * 是否是标准品类的必选服务。 - * - * - true:是 - * - false:否 + * 是否是标准品类的必选服务 */ private Boolean required; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java index b52abad8bd..b687326340 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -23,6 +23,7 @@ public interface IotThingModelMapper extends BaseMapperX { .likeIfPresent(IotThingModelDO::getName, reqVO.getName()) .eqIfPresent(IotThingModelDO::getType, reqVO.getType()) .eqIfPresent(IotThingModelDO::getProductId, reqVO.getProductId()) + // TODO @芋艿:看看要不要加枚举 .notIn(IotThingModelDO::getIdentifier, "get", "set", "post") .orderByDesc(IotThingModelDO::getId)); } From 0e20ca342f67b547fb181773f7008448fd248019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 29 Dec 2024 22:31:58 +0800 Subject: [PATCH 061/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=8F=92=E4=BB=B6=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/enabled.txt | 1 + ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 9347 -> 8804 bytes .../plugininfo/PluginInfoController.java | 18 +- .../plugininfo/vo/PluginInfoImportReqVO.java | 19 ++ .../framework/plugin/SpringConfiguration.java | 17 -- ...uration.java => UnifiedConfiguration.java} | 29 +-- .../service/plugininfo/PluginInfoService.java | 11 +- .../plugininfo/PluginInfoServiceImpl.java | 197 ++++++++++-------- .../module/iot/plugin/WelcomePlugin.java | 91 ++++++++ .../yudao-module-iot-http-plugin/pom.xml | 2 + 10 files changed, 258 insertions(+), 127 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/{ServiceRegistryConfiguration.java => UnifiedConfiguration.java} (55%) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java diff --git a/plugins/enabled.txt b/plugins/enabled.txt index e69de29bb2..b62400a815 100644 --- a/plugins/enabled.txt +++ b/plugins/enabled.txt @@ -0,0 +1 @@ +http-plugin@0.0.1 diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar index 0300a632ae6d0bd042e974113a7193dc0e578232..28b10d529ce689efb557305c356827c90e42e60e 100644 GIT binary patch literal 8804 zcmb_h1ymf_wuRshjRYsSyK90&aCg_n-Q696(+TeG?!kjYAP^upK?1>o6F!+thBuR$ zy!q?@+pDX)SD$@O-BY)!cI_=M1px^U_H8s*q-*^4<@XKT{ilqmvLL;rtQe#G?_scD zP4{8sdDFZ@_kW9kgMnfEXPAtjtfZKzk}`vgm`Yr$4FoZ&s3+N1eG`1?{^(>@}TkX;AA>bTZV-#x3gTuK> z+_6HQSy_SIkmnZ4ezE!`JBH{BYP5hmPS951y=Hx5&m z7RI(R-(M!ambOlC@vl!mi}eb%JA?W$^`AQgaX)n~Cv{mRrlL_kIxx zV1j|M{ksGbPEK|b1~$f4CJqcnRtAoaX?#K~We}nPB&I~Lk#T;h zNstt9#uVYO9NuZ}l7!M9@Rk8#s^zmSxo?xPcEr?Lis*K$u=Y)7DqL)Lbi-Y2JI~~_ zJPtfrH3p3V7gyfyj-OjUFFKChrhnP0zsul8(TDiDBM8j`)g)p}{4!g5@}|TJB>dH` zGq1$qEs-*`p+fVRyQQipn^ZKLqy-tx9^(8SrV6vI;UFE&!H7Q|_ai(H{Wyi1t6Fw7+2UJRyLgF1ScX z`g&D;UE=s6H0{x`C`Q+?JdZ<;k9b@rE8_LFWL@WEgjh53{_%dYRjL!R6y;EDWCTQO zp5PEs(pv%FiMN>bmG7hH5l23EYmcBa=>?%6U>`FU8V#}-Dg?VN$+NwXJc?0{S{4k5 zCJwCa5bHI>0Mz8Yz+lJTirSo-t(8K4In&qJVowjpat-XnlLFG9yeZk~XMuK=erH%? zgWRmpyAlBmNecud!c(Cu#K6}`^GM)xMWq(tI!q)=M>-7xtr46#8=UKAS?N%;=WTdC zIE)FhBH%TDb{mwU5iRSbsktE3VcsO;?|G)6A8<#`$GA%F46=CRIl4=$LJ~~Q)0|s| zW=}fH0&6g)PF_k+wh+tox!S`s+;u;W`kiL`40n-Gz26rSQDp9R+n_NPOf99QxKU(K zf*_Aj^ir>zKdA=!RMiQ;q}qP>Xc3w`Z4NTzr#8vFcu^Vp9W6}JgcGUNT0%C%1V0M2 z(K%@ns;nz|rRkinIw1vDoG=TT1kOv$V}Xm2-I;H!A*fv_UMW2DGnm~@4rw>wHl+d4 zWF;|yoN{642T%188p6##zS!MK7{!`r$`hEE8loo4fk@9g^{dmOXbmN=m14dInx@8E z#A`7~6$2wKT7pljjPhS2Cqx(rDIc!bt*gR!Vqy+An;l@-<9!%fLE5Wf+XcOpbPn;1 z1Wm6nDICs}=k{+d%;SfxA1k7CdS!Wr&*NvYrnR^>YfHp|%b&a!- zErCe;=}7xaNc;QN`!RrWAI|*hhU6r^&*?9 zV`(Y(LI`5zn98J6xb`-6!}W2?BVx`!uH3pQcEN~vp~4hZ$Rv6461XR-fP}KkmuOqm zm@wqhs?l3vOHkIc{0c?zUyEaJ>1AN?zQxWa7RTOQ63sCze%Rbb9t{1j4lOvAZ`0g$dM$l zrq0~r@Fl+CbEeE5H1N#FM^Ik&5nDhfy;6}5@v|b!TnP&#=g#XOv*m^{kQB^rn z8ykfyQ%)-Nfvq?ip|b|O85&jG(U~9<&!t!28t_&h8LOrQs1q^-oOwnocZasmK*jR@Yw%p5x>O3A~q6BYUhT*W&8R`_^UxEaB?d+_cP{0xt z#7GzcpG~`HRiE;YHbrM=r@jjC5-$nM?8=di5DQGCp=$jap1o0?OBouw%;~i@$opng32Hj69~?;oq+}?#0vI;w&%+<56G`m zDXNf?ywV}8I!K5)P}6g_^70PWJX!RF&MQ+@udoq-d+91SBv}HUZyvksrjxVDs=y(&&F4|>cG8BUwE zlkDj{;P=|3ycoLEQame9nit2s!p}W2X`e2*r|q}<>jIPm_F%QsENixm350Ydci=EQ zZ|%#t4jfHXR86k6=aW<(4e5;9J=$BFQ{Oymmdnnj`3Or>%0~`mb*E8h^)#Gg>!i2( z3u1s@<8Ui~41r4XE4@ZW&KSa%CNZ3^*S7Pm$Au3~t|9f1y?LT_7!om7k|0Sl2k`I) z1mzsK0sxxS`ger+b@7QLg^TOx=ryn{FjXkmI}B$u5W{)QJ^eQ}bOp%!8B4;~WqKW1?U%wRi&+T8_%jNQxIjf; z+1EuGqPeuQhL<}pFrye4(&oBrsuJjhf|9ZxKPA~ggT3o^0e6JDxiCHBfS)7R9=IFd zz_L?)mT?k14{-+})7Tab4^E%5EtJShO^il1lrsMzF~L7`>X6lg<$|7RHSzIa=OUX6 zgtHYE62gR0n2Pt2T)>lRd}=vq!sBK@t+QUs z@A+OFw>ZMIjL;qT#0>8SQ`(_AWMo&g0Xp2q^uF~4IelLZZthEJuWd4cH$8i70^Lhr zYd+$t?uGZ*L~I5$G1mx991~HPH|L{GAo56m98eJOTqN zhXMm*|M#p={x^n6#{8WYrYl>@p$lU03nNG^^r>QqCWgwJEWjfaQDG^(?ul0>D+b4V ziqx9mGFY?lNmi1{=a4Z?AD8V4;zprAt${C!0flqExz_cd+3{ek`Q+r?J0h@*bx}lu zF!Qk8ZIQ?Rz-Eebx^dHvX1&H@vjY#8C4(vI5s*@)-8(v1g#~lj>1jxvoHv1|i9uM5 z!J`9{(WxO#F_2CH%m--<=!VO6C2yV6j2P-afrg<9k?*>^niu#|G7%urNNdx&Fv-~S zoU}z51%1__0}gO2nL}Hno>&`482IDu7qx}#LG~CJK<&OqK4D*cGDrZUqNS^J3pQN# zM(E{Ca%$(m4f$qNE=o0|jlr`XYXVm$2_^@^$B&}lyS#u7(4TQ5debgLWemvF>%pTD|IeiXW@U8l=l|_>{lY4i9tz(n{en?1E44nfcddd`% zVC?8wQ(tm%RwTPDlV`!9dGt$qzh*C7$}cJXK{O`u`ePTJ_-Qf$X|>h92zvfG&ru6?vRNf7xL`A3=cyJU<>#BxDmpX-dY91S(o9YpVy(p_qtqfz5apoEe+nBb@3r>)p@19zn@2RMzCcE~~pT&?Ay8?#o_ zKEvQ6H}{q?Jx{^Mg{12WYm02ztQS$FcE%gh2=zi|%1@84RRAVYW{i4+4{>C9t4^jS z8>J{x>Kcm0q(i1>Du%5Q6V_)FWwU30b_m^m(Rs&Pkwm&Yp=PsUx`TR4Fn8r>QnbiB z)wSeXkv}{S5eKnoyCTTD5+qT%_*lIkOqD58ZBA%4?5&YWp3=-0NV%6T$m4m@Z(;_A z_tf3{tFtT;cQ6btZ=nCY;O&vJ^Fc6RV4)~rV66Y!1^@eUSG&~1_=t1Yp$#z%RznEa z4^LKv@SNgEg0?gqa??D8)K7l;G)xV8b|XE5b1GoZZOv*onaT7lD(?yAdzs6()g`CkXQZ2jdNr*zDdq zlIL}{cYagZylA8PgS>e-pj(n2rH-ze9$l$uvF6RngQF)zPyjv_cw$y7BKc62zG`+I zxHPfBYC4;_H`SbOgVi#${f9x_;w@~XIVT4`k)^ArXo2Hu1nLPbky<^PVb=)qQ2;nR zg-9xte>`D`85EXXH-DeydaL!AjZvss*|HloA_!RE$kX+ z*4Hb}w}9zjqerG>LWDyD36Oe;=T;-C^b2Y_VdZYmUvuCrce#Wh4a4C$u;q>$&wY}z z$SoBU*{PvF#%$IQg6jENPp6U#0&EE>Y!!Hv8uWqq>W4Nh32BX?=6oEwr&^{N=D2f@G6290dx3mnVXvdjs1~BA0Q{9w;W|l;OX_uts{!99DkFC?cW!brP|dSMW5g0DGEybxe;N(d>hi|~zP?Idb(&MsAruEN%UPF4eJ8v;*-cjD{V za!F`WR;r>N9|437%9g8pw!$V&W8~o(i~GU2=NYpzEJRr`0~gMU0J~~PfqM~xaU7$3 z9MWr#3UI7zq$zj1(b&Sq91;Nw??|qC(b(Xhp0$PGscc4rKr|YO=EZ&YjwP}Cl zbDo70y@nPy8NmTBBXVfAeGa8sRHF=Y*8)4^{{{0&Eid+2g+3;$aNwe>Rwmi5g8`Bx zVzyIZ6oV711T{@aU@7JVjpkdJKHM7CFWVZ0UTS%IGbe=j1Fpdfa&bs^uE%Gvcpa4M z>|U9I$X_fynl#v7x0tKRlwLeFafknSv`8y*Bi|pJrC0#TPC9akNIpWERtRW8B@QCD z!|~3NZS%HvG9E6TWo?qVGZQ7yfN(L1lWCSH8#ja+am9uoSYDQ!j@_*wup4TcshDH z%qeQl5Mj9rFzI`rO-#HcQG=tjd4tuNa99zwyv|~;=d;YIX#H}{gFb7Io&QmpM{5N( zv-y+U3R_Fzwm$4xLtMBrPQ+=|;9NsPn#s}ZEbbyU`#>$4<`)tUJnwmn(U^BzM3I6_ zOGB1OYgq88h;mP$cj4Laq1XZ&xrGI7oDrl3XUZ23IC}_6@{#Qwu;Jpw;@64Tja*xZ zlXV(!ajjHxsdMn{+fg~s)twTJXQb|VbiWFIf1EE>USYqCT*{6*@rNQfH_85Va#(7~ z1-B8KO(D@KlO7>T$z2@Up&l6&o7ILO-@()_0-7t)a1gTNL^@-A9qvlGZVFg_50nVLZp%kS0Kl^?K34wh68tV znwN=3)e=6Kx3z0~J~!@6*_QWnna%D5`=gGFTF8f+q)@<@|&h#|D6$>MuN z=1URhhvM(=!p}6Y1Jy=O4lAzN z7j2dZ$0&Ilwpb*)@yC)v_@V0Yt_AGlXrJ5Xg1kje?Lkm`gk zgN@2-%?rLPSl73nORw>MIFoEUCI^|>--wK2cu*c$*jfWG=g53e#pALW#J4Bp;(&;~ zXrp2pviKP7Tp@jd@*YEQl)(7}?TU5%1-1_vy_Ha#&F{GwjE%V8B)2T)XVok+Qb=qn z+Uu+)wnorZ*}vDRl*iK6u-h%&cpiZaFE5@NsvuSiB-hP3^9)*?i14Ue#jHb*wLzS) zI^ppMQ-Z+{$Pl4C`qb|p&?$9DTXz?B_6~Rl=dt8xC3@x??$_ZNN!}*us}GQcBERXA zBC2`!cujdn436p$OKzRhwc4JU!~H;kI*sk1}!r^5_jTHBRfT*{FBH0aV|ratPwo`DJ*jt*mU_(*%AWArOObP`wi zz7x-M6h&c^_S4K#ffQOF52cOnkmr4nov^W!bcj@PqWu@gBsX7MA5VT_MW`BMr}Y!@C|Vh}=RJ_p>q5qCoh;&V zxUtM<#`TJlWScxdp*|_O^Ztb-FD$zS zX6K{j6agorz2?2+eR0B|F%=@Z+?qh9UYbxn-3o4vI~gQbks4 zJOvv)747_p6(1j+vuAO$QJ;sWdiTNo-Dg2+RyFp=ecEM|1}-!ST~Uey{lskpBTVRF zO69hc%$&GrLno4RX4}}_E)lv+vd5l)-Lh6%)=KpQPV5=aY(`KO0VLlU!(E&1l+r;B zB{5p&!{Ni0DBfLCxb;%VLg`hRkPk5^&jo+2%JZNKL2`s7pN^c^UYRtbteJ{Q7>pPJ z&&F8{2CuVSy0*acn)WV+hiHR9j3tgd`Y_$=mPZ&f;Q)P|#~maLR+IX%F|>LcGF<>U z627Lu`;#~&+GI<{Vhu!^lbuf?)w@^jU22$5Cc8?$!c^Hqb>@sdVqtUO*fV7G^Q}x3Dy9U3PXXJJ5KD~Bl zqZjDlkWKkZr_keJNuR6TW)YvaE8P1kORT(N`ykyfl_^i9G)_uao4sLDmPBSew@vBx z)~3>0`n~vE3kE(!vnp!JjfXmJrLumEc4*Tm-m~F4(nyw3E-e_&>WUqB{tOi9`F`GK ztC*b@-A!c`p1t!9MZb|Hme)q_+*H0|Icj73>7Sp61~5Krdf#7I!tdYD{~sLO#wks_)B9b3ZNv!A!6=&EEW-ycGB&42XYrQtxMaKTyH;1;2g%hp+kr z_V>=}x8QFh&!6&s`v>eleAZtT{$@~qAKwa-+`F~E*_A&^{(K|)W>`J|gueqG?8|=! zelsv1fL4D7{;)Iu8UD@Ce1O0FJNO^==09ioW^g`anfyhT?{?=uXZmJ%K4e0;*Q@Ru z_oENrr}O*%e9QF5jr2#tzc)aCq|Eb|{&xv~vO^z^_1}wW4;2g%|J5#ktMF%O?XM;N zDqlR5P`al$|Dr1oCBBOp4+VZ+e&0yb_kjv_cPH@pKUVw?3iYGKKZC!Ks0Xm+&)}aa z)jucuMyei?75(>Q|Ak`xsPz4~{gq_>sFmmc=>AOj<1GI^=fCduFDv~{xgMJQz2ILj jx(@}7?oXQkYr%(0u)Gu$^taro_b=#sg;#>$+pqrt43(v- literal 9347 zcmb_h1yo#Fwyoe0q;Qwu?hxGF-8HzoySuxEpuyeU3JVZ4IKd$aZUGYDlSy|oJ?ZJ0 zzy7~#)qAy8oxRV!=heG!zq1vj!6D!PKZbm2vgSX&{C0qSJj;rz2+~Q)i7_br76t>5 zdkj1DpSx{;{2Bil0D$(-VX}g9l47FDD)h2qs&V6ReT=~1u8b%0|P93!ltsecL>XmAOG}VPW%*Dc(oz1f_R{ z;9EuNScbGx?(XiYv})-uWaKz@@%ZvMy?gxS@{*vw#A4^FymNLrKe0P5Ae7-%EVtmD zxtZHtjYg5u`+czhfTQ1^YXbhbSR>m%s_|>U^Pd40c1Cu_CXRm-i2Ub34;N!YyT1v? z`m1moJ7X7XlfOaX{SC^(&iS8^n2CR0D9L|7+FQGrSv(a21|a_Uw?LJJ4HJ2+fa&9G zS^ql%31??}2}4_BYZFI$BWpt^r%DZ2Z?rk=hwgFm`b8O_S`~WSoA4L&VEIB|)WITP zF9M8F2MQeOxFw<3xxb9jCh8G164+F_H@bfyz_1%2@z-i-Y_!z&%3WF#E>N{XI;`A$ z7`H|oHkG{nay(?(^>yuQ*VoPmH@??i4^uvX)Cg}#zJM|f=2z)VELGYGUCufzB%P)_ zIg^~9M3f?&n-iid9J6Ps>9FPZQ^C3gLJY+c+qN!5U-4=&mr@W{irG=;&3hJv8o$ku z8)V^zh}^oxASte%#k27k1%|Wkaw$t=C!~%jy_QlBXj*=rOe%w|n@=Cq;DaiO*eluSiiM7WMKZ*#DGa`S}OHt>~0 z7ZVn{qo|(CAztKvuRQ$M-2$W*M8`q7x}3 zua!WOkDh&t3hUt<B#N>X4_!xHV1uY|iIpvP!PM{yOuWj$2qYU-fn!xdrqK%+54g4M@sF~o|RAlsA?U14iudp2t$ zSniBN6?6nLP8qkst+#&9Q4q+!$+YVUR78VxAfHgGQCvSUTy)Vk@*HIvD~zl+Ie=_; zF{`v))@mkr)y1a{3Y!!h_i7yHIwY9MMhKk|9(8Rf?4L}}Qk|haEm9PGsaCeaSc(%J zPmPWDYyivmdD*V?`&m`z0FJO%sZ|K=yOwK`>zmV|cpTAu*M3I3sq?T>2nI4MLFh0SAd{*kxQE!mQ#9E7yukPxYTkQinoWLtqXFo;c*63W6?Ik>6;u@!wT4Upok}iR zoRBYTJBWqc$yF+h7x7)hcM+iqE{^-_(Vmmb_E)jdMKx)^<>aN6G-Gn!pE`9O(=7Pv zuxi2AE}3Q?w%xe2)tql&4WQYgQTqT#`G}o*eQ@gpgPPUL7ZCQj zDzUP0FX}_h6;AwnB3ki`iq?vzWW(D&>r?Cxdkf&M;Kq1b2pY+hA%x?~-QnZjsZGdWdwPeoQ`WeWmet`jO65uq zUS#TkEGvj{2jmWte7>N9eVTT#;@^lhs5mzm8MPY#8*# zwPU9!49YKMXl(4>hNpZzL)f{pU)$Kdp5F`cV;-+%R>nwXKK@k9m(RxLgbO*e%|@Y2 zD)8MD_R*=YqDBLy)^(7osYVveu$NOuL4w|d{3<6w4%fP3e%L1bDCXS36nI!BsJ*)s z)V|c!Toxx~F)p%ZBI=ULIpnyd`X$$7zMzZ#HvF^<@yr=leLe)yg;-40?>ODq)|_b+ zTbB_H;|Hyj+Frb-yVdgC7SeBpx**o4bJ z#`HnqqaNEt+1{!0aNX(4+k(n|Xj}Udg2g(kZ{?At>4TrtSZqKSp2B>36mjkeYLZmm zAq_aIlCpXt>nl?@+wLBUCesi|kL`&2IUuj2y!o{wR7B_*u9vi-nP zG;HPgr7_(|%ct(!U3Je@!hOcZ6@+Kzq7#m+lM`1;yAPLvQ3FGfnIS*7Hc>Q+VmTQ~ zW%QfOez=qOfHh1?FPpui0NphBm6M(x)vw(hdu0KK-1>{IpdR6u9iB5qWOa_I@GJM% zG;#X6y2RTP<+o2^d+(+0{h&*|r{Ta( z^5P3TeSN+|y>mTRzlVu*bJ*@T{LVeH%8BY5JmLjG?!>yfHHbDG7ExHJ!fvj_+@DF< zeIX$4trskgetvNl*_lp1se;W_M<}lTt^eL93B0+0o|WR@E5pIi3T`c_3dR);fi89# z%r|fXUTziiD|z}8C}rVu&S871d;F8VoNRu&2XYx|8b9E3;izwloiqpU^)+;XSD3UF zYVJp+ft~%4z7=JN!Qft#D!MJ;&&z{Mln0uIfU7OIhs1aXL!sghh(Ioe*~K__mxSbb zRQk_Drs~FqCF&Rb+f8ALc=Rwd8$$GlA&26NXgo>CXo#V=i18;0g0S2QgBettIffH) zNRDNR=lM06oKcgyq2)C(NFcLCmYvhAy`?P++VzhGu%Bx2#IH?r4(bbJcpwyZxOb3OEvXzS;Mqi@nNEoo zEKOdP>0vM3-Ssrow0@;KjuZ$Hf(1U~)7mJ!gV{oE-Vx%SS&3IaB*7(s%*)ob!fP~y z=bU)(6jZW~dP-{PjDs*MP^ksmz-k!vc3gqld}zEyKQn$7v2WL@w9raOa#eKnSm|s0 zs9Whb9aN0zn?gY}%zd_#L$ftFAmfH13kvmhN9*0nOAP6?b)8udi;lp!K#qWo0IvW- zPd!+k=hr+{)rf*SL&iHSXDbF)g=D&PWL$EV~6QMxUi>m(Vg15 z?wB5<@V)K6j6xEf0m?e=^DJd#9m*aNFHb2(Q@+woINj|b+{;0eS8tzb8$Z-%WkJqF zsN_d4pREk&>z$ICUTiV+TVk{f!KfqTwtumszhqx?==K}pzeb4P(!F2n_iYB&1j6PT z)LsLAe}DO@%FVBfS%$#?04^v1fbG9m3j{hMHp@SxT{oPw>P|gKg7(NDd>~DXtz{V#wCEP>yh*B zn$!D?RyW`8-_P)Sh}Ga5m^7NqBtKr^BWr{qgnj$abJ|%DM;=)JtUfixPaG@$w&8_X@;H3}ZQHNRp z;Uh=1X-<@QZ!(oRw!qp^*g>-7In<1Gz z)ei2mTIFWoXAUxka#rq+wtL@kD{%X3{O0!rzML6{r+pVQh+->kCv~7U@{A+24?Rk( zaMxi2TcT?i;5=8OZIEjc`Gms2yL|a&x;)!CB?7Z84b=q%?xGa|zY7I|prNmxhjh`I zFD@x}>8@fFdWlz{9!HjREBZzg*9WR?*mcG_wL&jKUdFdL5B6MnKj}@avO4sc#I$+n zIY$+-=oO9+K0H2QqWO@^N_Y<+N)413w?{M|b+KKGa;y(Xp*135n(3&?aPK&$*3q&f zBI4mlHiaS<$R9X2ur<7qtQC0%CVhdzGlU1X(Ca8se@yBpqWpgMG`U1Pc(1Xy9!MS+ zGD(jD4?{1l5TZcxyz%o#$Nhvm$Uom2?6=wWq%Z&g<>T8p%m4P)_<3XGsC)ULsbl-f z(aY3j0PDc$@)7}g)?kKOQYkfPSx}C7S@DVKFlvVf2P3A##-=$Lvf+)EHkD|U2CB)K zP17X<#6${?MgH@amqy0m50|(l#~F(&!;QN`cJDL3cFgZ}X7|5+!0rTJ$NnxY1SK3| zO5IkZ$z)5@Ig)NkrK?73VNcv9)h99NqFm~iUwtiHgmPKr;c1 z4hJ6sQXtb1?75~J-fa|q`mEcrcJKk8*YbpVra@Z)=}HVBU+>` z6A|Cm(BKmOW^Q1&ta!N$NAfi++Sddh-)t+%`MRvu)k>@EjniSKMs3x^DqG4-6HCeE z-Qse*^l|aBb0eLDtMV$Xlu7L5;a3~wu)2%<%Kog|2XpYNv#UO6E+rKy9hKx8fDh}) zuvXXv9#}y*sw9}6COnZY@U0FxKmL`06TieBL211XS7P z;LlU%Sa%dIu;z6bz*U*yYN(;-;2GpDe6H6UUroTb$#r< zPn5p5&Gogn*}n&sZ@GQMCnRX+x2I#4BX4OJWs54CXVtHyadT8AK~0oDhbHLBYY2N>dJFM$C)21xbpvJEv`w zPI^mWmVC-A%xdV&n?5MhG7HL-Xx2~N(u%;Fgn2$e&p`8{j3!VG+vXZa`1MSJvyocd zqWLB8yka*3cwN}uLnDRUWS_B&(j~Vg*Nm3bZd;x2Qc^sE03wwtu*KgeA0kdoDQT=U zCiXA54&6VoOO(&^oH`RfO(cRK6TxPeEsfAhm2Oo-jkMco9XST5bddVOl(|kuzmI)B z4v(KVj7MRrjl?jbfT6h*Py>N8!@XDHNL!iIp5*XNXq=#OX%8spHp)0D8bnM7QsC6L z8`~09RjJ72oaW3bP&ftioa%9-+>S zbpL61hUZ<7dO8Pv0p`a%oQTh+(S|H3qk*F{qPbzmn_eh-c{D~2a@Ze-kVK>}GI3OO z%q7P;uU`mz=t@f~kBEHKE=|Hz#)MMZt16O1l*#5uO-<@f}d$$XWE9*;#A3XOdI6Z3k^_C~)^{Aur)I)uhW)fUNmJH7>97_}n~G8{r_< zP%d3UH)=)uhEpfsC{vig^?6=b9OD%q@qt|5gle3cV zU*rzgaIvzm=Hg&j`jQnO33__&qgB~ZpgLc6iZ@@&MDzL&g;mRBcN6zqH)4Ijq(X`c z5)`W-@Do8HxVx?l{>X^NF!hz$H%yxKh&)W6jt$379!IRYq!U|$p3$D{yYE>MbBZVf ztsDb#48_1qVt$NPtnwG6@)+q@VzBO^I_}n-)Zv`ZSZ$K2Gvdw>4PHk~$+W7Q7Y&yS z7)Sw|-@^$g&)>*?^{G#3VACsziVOUdQ7de(JR)<*1Pf^eIQP^UnucXL5Dm8RijPA5bkT;S30-aq<{JFAzMXWLY! ztWJnXky2hYoEAvmHXgy~QWj13s|I1gM?fXf2fT_wOkV2njMonhsQH%-a9X}MO)rv@ zk&I^Of{Q`wo_tj#UZUF|PkE4*K452IS95&G^?eY2AA=ZSUm)|&%9jE9mGX{&{B=~4 zg^Yl+^&`WB*09y)6YCps_bb#2w6OOv27XXgsAMX9ky;8je7OPa8?wcF<4{Niuiz|E zJ!&&Ls6cAgf-oivviC5@#ve77+A(=FF^D%esNyVV2r7}qvV7u(cVW6ly{%Y`J_>$7 z13QkuUYaj#Gz+IbOQ}Om9zhpr=LszkuM0hSC1M?VQW?rr(qI{bzTAXFjB$W9Q0t{I zWI#YgDD(;_r!8ve@R7(X$k(`%GjkGZ0d3gRYRt7NakF zxEsWAJ35IwKkBmB$+H}ruW8bht4X|B4fNlRtovNx-T-lUPod0tV_&}Yw=Dt&=&A(E z&y3ho`%4A-AO+wTd*wr^25&qsAzAbsZ~*GMzuv~(|6oT-hn$^%x7^Szz;MX7^mXQ5 znAHP#bZ#gmoF0X=zs($=5$<*Zy<`&K7}OG2zAcSTc8hZ_F**3@JC`j`w$Pck?q1r~ zOv(V|_EW9~bUunL)Oo~BYFd^cd=ZOl*uc@|`|IcTQprR)87B|-q4Fp4%-5y!k80`R z4?X^?$Aa`II{xrjYz$pZo?7a^1{3}oY-CISo7F)7%I;4M{eN|Hc>Xi^A07nVPg8;J z(H@{Pe{9av{nF|GVHE^X?YzEzG(%V(7a;t9WVW}np?9~jo>Lo-U1kS%9V^2KRS|dc z!$gLt2_~?b`$K5H87EfP%D1T^OC+N5YVIZQ94j?PCVGqKXr=!qE#Gd2+$S1Q^lmD( z@yM0obOgtj<-6%F3Fm1+IWdx&iUs+#rh7QKp@0W9^WdJ@M)`5Bn0`oHVvq*4gQPO5 zj)iO43?$vKQv!-ICtY;HTP#dtQBLB-9%dBF*!TKfJrw4F1lFNRYlvYE>KVY>PO zSPq@y3nxVW`XPLfdX+<-onl|M;JjEf@HXceUi+yl#$wHfo+O9DwhV?0oBM7k$(MyQ zU0Gw0M&z%M&Z(X1L@4pSOH3vqee3vaIlP}UeYyP^N-W57y}9P5wY&NJvX2e2zKz#9 zpp)5ELBbhiFceISL08AtHsRSA4qaz1J3lcwLn481QMx`hf!1I~H|r>o-es9BZ0vxH z_uQl;@`E!Mk!(lnrH#(^!M3Bb-Scp~fWtdLnR1Dkk~(En?YA!QtkWdVcTB{I2z6GT z-`8XEX-ps1-1qk6$ehgRFhMG`;uh@jimwo7OB|BcFqJnFq|4Goso_Q(Z}}4qz#J^c zE-AmkAt&$<7I_CEPW2mIBs(O2%#U1ib2@rU_U_I~u9>^#cz0%Rmq4*EhWcjW<=_@9 zZS!Z?cNdYxMV)yrW(Qf{)N1;3bj}FV;prpbOjS`engSA@=L=er>Lzklw96ARW0KBy zRW^~w1D!$l=ej5U^4V~FM(0WiHf1$%X)D>8vMjVMXbn>Dg~ED+;`C=Vvw}jnlB9vQ z4Oxu-X%h{#I^6l6Ve`1)PcWEx251Ml>myDD_&o>ZSQaqH`EbAzL9E?t@}lDV*S;z$ z@nkk%yWEhGjSjx2AC?w&e&Hr>brnFdR4!hX0?Q9VPIFkw?=>Pm zj5%|`2lr$TT6trjeWj-tM71KwWe;9qsH*9GxvpoDkj# z!u1mY1*SUR0_)U7q+SG6XyLT&mv#D2Sun?QF~01AF=kF=QEW&@C;m>`*e@*eP9h9dO z$H4z(il6 uploadJar( - @RequestParam("id") Long id, - @RequestParam("jar") MultipartFile file) throws Exception { - if (file.isEmpty()) { - throw exception(FILE_IS_EMPTY); - } - pluginInfoService.uploadJar(id, file); + @PostMapping("/upload-file") + @Operation(summary = "上传插件文件") + public CommonResult uploadFile(@Valid PluginInfoImportReqVO reqVO) { + pluginInfoService.uploadFile(reqVO.getId(), reqVO.getFile()); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java new file mode 100644 index 0000000000..9216d09ecb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Schema(description = "管理后台 - IoT 插件上传 Request VO") +@Data +public class PluginInfoImportReqVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + private Long id; + + @Schema(description = "插件文件", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "插件文件不能为空") + private MultipartFile file; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java deleted file mode 100644 index d87a94f318..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.iot.framework.plugin; - -import org.pf4j.spring.SpringPluginManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; - -@Configuration -public class SpringConfiguration { - - @Bean - @DependsOn("serviceRegistryInitializedMarker") - public SpringPluginManager pluginManager() { - return new SpringPluginManager(); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java similarity index 55% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java index 3450b67fb9..66adc3c9fc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/ServiceRegistryConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -1,35 +1,36 @@ package cn.iocoder.yudao.module.iot.framework.plugin; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import lombok.extern.slf4j.Slf4j; +import org.pf4j.spring.SpringPluginManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; -import javax.annotation.PostConstruct; import javax.annotation.Resource; @Slf4j @Configuration -public class ServiceRegistryConfiguration { +public class UnifiedConfiguration { + + private static final String SERVICE_REGISTRY_INITIALIZED_MARKER = "serviceRegistryInitializedMarker"; @Resource private DeviceDataApi deviceDataApi; - @PostConstruct - public void init() { - // 将主程序中的 DeviceDataApi 实例注册到 ServiceRegistry + @Bean(SERVICE_REGISTRY_INITIALIZED_MARKER) + public Object serviceRegistryInitializedMarker() { ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); - } - - /** - * 定义一个标记用的 Bean,用于表示 ServiceRegistry 已初始化完成 - */ - @Bean("serviceRegistryInitializedMarker") // TODO @haohao:1)这个名字,可以搞个 public static final 常量;2)是不是 conditionBefore 啥 - public Object serviceRegistryInitializedMarker() { - // 返回任意对象即可,这里返回 null 都可以,但最好返回个实际对象 return new Object(); } + @Bean + @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) + public SpringPluginManager pluginManager() { + log.info("[init][实例化 SpringPluginManager]"); + return new SpringPluginManager(); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java index 8671034021..d023791c81 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; +import java.util.List; + /** * IoT 插件信息 Service 接口 * @@ -58,7 +60,7 @@ public interface PluginInfoService { * @param id 插件id * @param file 文件 */ - void uploadJar(Long id, MultipartFile file); + void uploadFile(Long id, MultipartFile file); /** * 更新插件的状态 @@ -67,4 +69,11 @@ public interface PluginInfoService { * @param status 状态 */ void updatePluginStatus(Long id, Integer status); + + /** + * 获得启用的插件列表 + * + * @return 插件列表-插件id + */ + List getEnabledPlugins(); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java index bd7efa8d0c..333d0fa988 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java @@ -1,17 +1,13 @@ package cn.iocoder.yudao.module.iot.service.plugininfo; -import cn.hutool.core.io.IoUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.infra.api.file.FileApi; import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; -import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; -import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginDescriptor; import org.pf4j.PluginState; @@ -22,11 +18,13 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import org.springframework.web.multipart.MultipartFile; -import java.nio.file.Path; +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @@ -47,9 +45,6 @@ public class PluginInfoServiceImpl implements PluginInfoService { @Resource private SpringPluginManager pluginManager; - @Resource - private FileApi fileApi; - @Value("${pf4j.pluginsDir}") private String pluginsDir; @@ -95,6 +90,19 @@ public class PluginInfoServiceImpl implements PluginInfoService { // 删除 pluginInfoMapper.deleteById(id); + // 删除插件文件 + Executors.newSingleThreadExecutor().submit(() -> { + try { + TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 + File file = new File(pluginsDir, pluginInfoDO.getFile()); + if (file.exists() && !file.delete()) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFile()); + } + } catch (InterruptedException e) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFile(), e); + } + }); + } private PluginInfoDO validatePluginInfoExists(Long id) { @@ -116,73 +124,100 @@ public class PluginInfoServiceImpl implements PluginInfoService { } @Override - public void uploadJar(Long id, MultipartFile file) { - // 1. 校验存在 + public void uploadFile(Long id, MultipartFile file) { + // 1. 校验插件信息是否存在 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - // 2. 判断文件名称与插件 ID 是否匹配 + // 2. 获取插件 ID String pluginId = pluginInfoDo.getPluginId(); - // 3. 停止卸载旧的插件 - // 3.1. 获取插件信息 + // 3. 停止并卸载旧的插件 + stopAndUnloadPlugin(pluginId); + + // 4. 上传新的插件文件 + String pluginIdNew = uploadAndLoadNewPlugin(file); + + // 5. 更新插件启用状态文件 + updatePluginStatusFile(pluginIdNew, false); + + // 6. 更新插件信息 + updatePluginInfo(pluginInfoDo, pluginIdNew, file); + } + + // 停止并卸载旧的插件 + private void stopAndUnloadPlugin(String pluginId) { PluginWrapper plugin = pluginManager.getPlugin(pluginId); if (plugin != null) { - // 3.2. 如果插件状态是启动的,停止插件 if (plugin.getPluginState().equals(PluginState.STARTED)) { - pluginManager.stopPlugin(pluginId); + pluginManager.stopPlugin(pluginId); // 停止插件 } - // 3.3. 卸载插件 - pluginManager.unloadPlugin(pluginId); + pluginManager.unloadPlugin(pluginId); // 卸载插件 } + } - // 4. 上传插件 - String pluginIdNew; + // 上传并加载新的插件文件 + private String uploadAndLoadNewPlugin(MultipartFile file) { + Path pluginsPath = Paths.get(pluginsDir); try { - String path = fileApi.createFile(pluginsDir, IoUtil.readBytes(file.getInputStream())); - Path pluginPath = Path.of(path); - pluginIdNew = pluginManager.loadPlugin(pluginPath); - } catch (Exception e) { - throw exception(PLUGIN_INSTALL_FAILED); - } - - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew); - if (pluginWrapper == null) { - throw exception(PLUGIN_INSTALL_FAILED); - } - - // 5. 读取配置文件和脚本 - String configJson = ""; - String script = ""; - try (JarFile jarFile = new JarFile(pluginWrapper.getPluginPath().toFile())) { - // 5.1 获取config文件在jar包中的路径 - String configFile = "classes/config.json"; - JarEntry configEntry = jarFile.getJarEntry(configFile); - - if (configEntry != null) { - // 5.2 读取配置文件 - configJson = IoUtil.readUtf8(jarFile.getInputStream(configEntry)); - log.info("configJson:{}", configJson); + if (!Files.exists(pluginsPath)) { + Files.createDirectories(pluginsPath); // 创建插件目录 } - - // 5.3 读取script.js脚本 - String scriptFile = "classes/script.js"; - JarEntry scriptEntity = jarFile.getJarEntry(scriptFile); - if (scriptEntity != null) { - // 5.4 读取脚本文件 - script = IoUtil.readUtf8(jarFile.getInputStream(scriptEntity)); - log.info("script:{}", script); + String filename = file.getOriginalFilename(); + if (filename != null) { + Path jarPath = pluginsPath.resolve(filename); + Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 + return pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 + } else { + throw exception(PLUGIN_INSTALL_FAILED); } } catch (Exception e) { throw exception(PLUGIN_INSTALL_FAILED); } + } + // 更新插件状态文件 + private void updatePluginStatusFile(String pluginIdNew, boolean isEnabled) { + Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); + Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); + Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; + Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; + + try { + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew); + if (pluginWrapper == null) { + throw exception(PLUGIN_INSTALL_FAILED); + } + String pluginInfo = pluginIdNew + "@" + pluginWrapper.getDescriptor().getVersion(); + List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) + : new ArrayList<>(); + List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) + : new ArrayList<>(); + + if (!targetLines.contains(pluginInfo)) { + targetLines.add(pluginInfo); + Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + + if (oppositeLines.contains(pluginInfo)) { + oppositeLines.remove(pluginInfo); + Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + } + } catch (IOException e) { + throw exception(PLUGIN_INSTALL_FAILED); + } + } + + // 更新插件信息 + private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginIdNew, MultipartFile file) { pluginInfoDo.setPluginId(pluginIdNew); pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); pluginInfoDo.setFile(file.getOriginalFilename()); - pluginInfoDo.setConfigSchema(configJson); - pluginInfoDo.setScript(script); + pluginInfoDo.setScript(""); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginIdNew).getDescriptor(); + pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription()); pluginInfoDo.setVersion(pluginDescriptor.getVersion()); pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); pluginInfoMapper.updateById(pluginInfoDo); @@ -190,52 +225,50 @@ public class PluginInfoServiceImpl implements PluginInfoService { @Override public void updatePluginStatus(Long id, Integer status) { - // 1. 校验存在 + // 1. 校验插件信息是否存在 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - // 插件状态无效 + // 2. 校验插件状态是否有效 if (!IotPluginStatusEnum.contains(status)) { throw exception(PLUGIN_STATUS_INVALID); } + // 3. 获取插件ID和插件实例 String pluginId = pluginInfoDo.getPluginId(); PluginWrapper plugin = pluginManager.getPlugin(pluginId); + + // 4. 根据状态更新插件 if (plugin != null) { - if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { - // 启动插件 + // 4.1 如果目标状态是运行且插件未启动,则启动插件 + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) + && plugin.getPluginState() != PluginState.STARTED) { pluginManager.startPlugin(pluginId); - } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { - // 停止插件 + updatePluginStatusFile(pluginId, true); // 更新插件状态文件为启用 + } + // 4.2 如果目标状态是停止且插件已启动,则停止插件 + else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) + && plugin.getPluginState() == PluginState.STARTED) { pluginManager.stopPlugin(pluginId); + updatePluginStatusFile(pluginId, false); // 更新插件状态文件为禁用 } } else { - // 已经停止,未获取到插件 + // 5. 插件不存在且状态为停止,抛出异常 if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { throw exception(PLUGIN_STATUS_INVALID); } } + + // 6. 更新数据库中的插件状态 pluginInfoDo.setStatus(status); pluginInfoMapper.updateById(pluginInfoDo); } -// @PostConstruct -// public void init() { -// Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS); -// } -// -// @SneakyThrows -// private void startPlugins() { -// for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) { -// if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { -// continue; -// } -// log.info("start plugin:{}", pluginInfoDO.getPluginId()); -// try { -// pluginManager.startPlugin(pluginInfoDO.getPluginId()); -// } catch (Exception e) { -// log.error("start plugin error", e); -// } -// } -// } + @Override + public List getEnabledPlugins() { + return pluginInfoMapper.selectList().stream() + .filter(pluginInfoDO -> IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) + .map(PluginInfoDO::getPluginId) + .toList(); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java new file mode 100644 index 0000000000..18b9c1417d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.iocoder.yudao.module.iot.api.Greeting; +import org.apache.commons.lang.StringUtils; +import org.pf4j.Extension; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; +import com.sun.net.httpserver.HttpServer; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * 打招呼 测试用例 + */ +public class WelcomePlugin extends Plugin { + + private HttpServer server; + + public WelcomePlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + System.out.println("WelcomePlugin.start()"); + // for testing the development mode + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + System.out.println(StringUtils.upperCase("WelcomePlugin")); + } + startHttpServer(); + } + + @Override + public void stop() { + System.out.println("WelcomePlugin.stop()"); + stopHttpServer(); + } + + private void startHttpServer() { + try { + server = HttpServer.create(new InetSocketAddress(9081), 0); + server.createContext("/", exchange -> { + String response = "Welcome to PF4J HTTP Server"; + exchange.sendResponseHeaders(200, response.getBytes().length); + OutputStream os = exchange.getResponseBody(); + os.write(response.getBytes()); + os.close(); + }); + server.setExecutor(null); + server.start(); + System.out.println("HTTP server started on port 9081"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void stopHttpServer() { + if (server != null) { + server.stop(0); + System.out.println("HTTP server stopped"); + } + } + + @Extension + public static class WelcomeGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Welcome to PF4J"; + } + + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index 200a451b62..1ecf140a47 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -24,6 +24,7 @@ cn.iocoder.yudao.module.iot.plugin.HttpPlugin 0.0.1 ahh + http-plugin-0.0.1 @@ -104,6 +105,7 @@ ${plugin.class} ${plugin.version} ${plugin.provider} + ${plugin.description} ${plugin.dependencies} From 1a3c6756ab12b9eae2c8e7b71c71ad7c4fc2c2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 29 Dec 2024 22:33:16 +0800 Subject: [PATCH 062/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=20HTTP=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=EF=BC=8C=E5=88=A0=E9=99=A4=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/plugin/WelcomePlugin.java | 91 ------------------- 1 file changed, 91 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java deleted file mode 100644 index 18b9c1417d..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2012-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package cn.iocoder.yudao.module.iot.plugin; - -import cn.iocoder.yudao.module.iot.api.Greeting; -import org.apache.commons.lang.StringUtils; -import org.pf4j.Extension; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; -import com.sun.net.httpserver.HttpServer; -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; - -/** - * 打招呼 测试用例 - */ -public class WelcomePlugin extends Plugin { - - private HttpServer server; - - public WelcomePlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - System.out.println("WelcomePlugin.start()"); - // for testing the development mode - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { - System.out.println(StringUtils.upperCase("WelcomePlugin")); - } - startHttpServer(); - } - - @Override - public void stop() { - System.out.println("WelcomePlugin.stop()"); - stopHttpServer(); - } - - private void startHttpServer() { - try { - server = HttpServer.create(new InetSocketAddress(9081), 0); - server.createContext("/", exchange -> { - String response = "Welcome to PF4J HTTP Server"; - exchange.sendResponseHeaders(200, response.getBytes().length); - OutputStream os = exchange.getResponseBody(); - os.write(response.getBytes()); - os.close(); - }); - server.setExecutor(null); - server.start(); - System.out.println("HTTP server started on port 9081"); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void stopHttpServer() { - if (server != null) { - server.stop(0); - System.out.println("HTTP server stopped"); - } - } - - @Extension - public static class WelcomeGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Welcome to PF4J"; - } - - } - -} \ No newline at end of file From 8ca9bebfd1c8cdac3f64842df1d7c640ce68fa1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 30 Dec 2024 09:33:56 +0800 Subject: [PATCH 063/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=9B=B4=E6=96=B0=20HttpPlugin?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E6=9E=84=E7=BA=BF=E7=A8=8B=E6=B1=A0=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E9=80=BB=E8=BE=91=E4=BB=A5=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E6=B4=BB=E8=B7=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iocoder/yudao/module/iot/plugin/HttpPlugin.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java index 70da0131ce..66e0c69a39 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java @@ -19,12 +19,12 @@ public class HttpPlugin extends Plugin { private static final int PORT = 8092; - private final ExecutorService executorService; + private ExecutorService executorService; private DeviceDataApi deviceDataApi; public HttpPlugin(PluginWrapper wrapper) { super(wrapper); - // 创建单线程池 + // 初始化线程池 this.executorService = Executors.newSingleThreadExecutor(); } @@ -32,6 +32,11 @@ public class HttpPlugin extends Plugin { public void start() { log.info("HttpPlugin.start()"); + // 重新初始化线程池,确保它是活跃的 + if (executorService.isShutdown() || executorService.isTerminated()) { + executorService = Executors.newSingleThreadExecutor(); + } + // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); if (deviceDataApi == null) { @@ -79,11 +84,11 @@ public class HttpPlugin extends Plugin { future.channel().closeFuture().sync(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - log.error("HTTP 服务启动中断", e); + log.warn("HTTP 服务启动被中断", e); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } -} +} \ No newline at end of file From cbfbc55cd8612296978b56a122972a35e0e4c5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 30 Dec 2024 12:01:58 +0800 Subject: [PATCH 064/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=B7=BB=E5=8A=A0=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=92=8C=E6=8F=92=E4=BB=B6=E5=AE=9E=E4=BE=8B=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85=E6=8B=AC=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=9A=84=E5=A2=9E=E5=88=A0=E6=94=B9=E6=9F=A5?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=94=AF=E6=8C=81=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=92=8C=E7=8A=B6=E6=80=81=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=EF=BC=8C=E5=90=8C=E6=97=B6=E4=BC=98=E5=8C=96=E4=BA=86=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=A4=84=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 8804 -> 8881 bytes .../enums/plugin/IotPluginDeployTypeEnum.java | 10 +- .../iot/enums/plugin/IotPluginStatusEnum.java | 10 +- .../iot/enums/plugin/IotPluginTypeEnum.java | 11 +- .../PluginInfoController.java | 13 +- .../PluginInstanceController.java | 64 ++++------ .../vo/PluginInfoImportReqVO.java | 2 +- .../admin/plugin/vo/PluginInfoPageReqVO.java | 17 +++ .../vo/PluginInfoRespVO.java | 4 +- .../vo/PluginInfoSaveReqVO.java | 4 +- .../vo/PluginInstancePageReqVO.java | 2 +- .../vo/PluginInstanceRespVO.java | 2 +- .../vo/PluginInstanceSaveReqVO.java | 2 +- .../admin/plugininfo/PluginController.java | 116 ------------------ .../plugininfo/vo/PluginInfoPageReqVO.java | 59 --------- .../dataobject/plugininfo/PluginInfoDO.java | 16 ++- .../dal/mysql/plugin/PluginInfoMapper.java | 25 ++++ .../PluginInstanceMapper.java | 4 +- .../mysql/plugininfo/PluginInfoMapper.java | 36 ------ .../PluginInfoService.java | 6 +- .../PluginInfoServiceImpl.java | 17 ++- .../PluginInstanceService.java | 6 +- .../PluginInstanceServiceImpl.java | 9 +- .../plugininstance/PluginInstanceMapper.xml | 2 +- 24 files changed, 126 insertions(+), 311 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininfo => plugin}/PluginInfoController.java (86%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininstance => plugin}/PluginInstanceController.java (76%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininfo => plugin}/vo/PluginInfoImportReqVO.java (89%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininfo => plugin}/vo/PluginInfoRespVO.java (96%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininfo => plugin}/vo/PluginInfoSaveReqVO.java (94%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininstance => plugin}/vo/PluginInstancePageReqVO.java (94%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininstance => plugin}/vo/PluginInstanceRespVO.java (95%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/{plugininstance => plugin}/vo/PluginInstanceSaveReqVO.java (94%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/{plugininstance => plugin}/PluginInstanceMapper.java (89%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{plugininfo => plugin}/PluginInfoService.java (87%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{plugininfo => plugin}/PluginInfoServiceImpl.java (95%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{plugininstance => plugin}/PluginInstanceService.java (82%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/{plugininstance => plugin}/PluginInstanceServiceImpl.java (83%) diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar index 28b10d529ce689efb557305c356827c90e42e60e..25120abe7f4494bd468cca84c8cea531193ff425 100644 GIT binary patch delta 3218 zcmZXWX*d*I`@qMNMhqFs3}%`^BV)-<64^DfFPW6c+DK%VWo%^|W0|s>Fi42-geHO5v_2Y=Q$V6D zZGWIZXZaJ6&NIOoG-X0D7{nyWU(1bL4j$W8dY zY9H5)NuTA(T%YCT%1RADLu(mC^<@9a&;4=I%!A%W->~HFkgdFX7QK^O>(rJ)iwwnQ zcC!_)Q92$q7mQ4JedEyF0~X-x4Pd1+`pUNj=2XFb>}~aUSL)SRmcdf<5ohA-q3UDl z9LBK2h+p1yLDCxhKw&)>i3oQCoWW{6(Ju1vB`52YqMajG0913lov(#idBQ9Rq|E-P z<^6Dp`^ByiuLMK@*wQ1&Xop3z2F>ANZF~DreJlO3JW;(BTpz^*3h?e0G>*^{H)KDTl;xI4zxuAR5l&~B=36n#yN^z4|?w}DVnG8Z4{ zFJ344doM4S2VwdiXp*Y9WbxKAA|H(-drZW2{)U*0_a*gZ^#!~Z*>0cy4O8w&@m!Hm z_Il$ReaLn9_iH)JZ?HQvhZ-XTi|fdCBwXox3Cs&qc;w6`WTWJwvnceG>1|H+ZwQYfQMny^sGPO@|p9@k3 zOB&S=LUvc5HWxH4YPjt+)Vz5q6bbs+u%WiGrT$a#_iise6i_*PM%oEsp_N9syCB1t zWo;1WyS{FowPRx-5Aptid*rg=h!5=p!h2Se2R|9@IXu8x8_>Y0G z3ANr5<%2Q+fq`JZCW+S7yc2{ZMKYov+nqXbO2;ik%n)dtyf3UcW}^@7Po@AT=c%zx$DQ!nuI}wl>mFgr1(ZxEGA{21&r;FBv5X*Tx%iW-UwN4c& z*%s3S)k;3QFJkk4A*56lfvp(DY=@X&u>i(iV4Azn>D?a2CQ92@m@?tuFJPx$3`rMwWW6>T}Lc@H-6pah4X zT6e0#`K-dK*w%e7ysgnO@8Kw|rb!Dm|BN1MGEv>1MvkF;zjp!=23DxP=c~<8r94Br zDP^|r4zF~RR7_m$nhUh`fVq6)VciJh^QQ0AQUc}jw)!95W2X{i&P@$V5seEep?cxM zJ&mG^H}IyuL0q-9O6kU+oW!S8p(X`Llb1vo&TDPqA^o|@a|1PMm_*oN$HEGoJm9!N zwMU|x(|EMG&yEp-zk;$af;896b|9W>_BK+M*)e9~=nk`f(~|hN^_M3yZmH;stiG4! zxuxM`^CtGX&b~gqD*9=Aph?Esm>ANW9k}B17%#S-oHS3*jDFgw67dr{d_Kj{Qr3Kj zLYzwrX!UJ0sh2Z)DK>;+KU3J2c!y$REveOAzT_6Jf45uVhDxG5xTixUW^2gT^(Al1 zala(d&qI5d0BSyOA4YSDUoK3%)RdeyM@ry^4i^Sb-v2q@TWLc4l0gmB4qPrEgzZ0? z!T9GncwPph8fqsb?YyW*m4UIzCFnz`js zc0Ju4>^!&K%n~8i&_{R5-aDDeeqM1I_4JqUo196@;99|0`&bxCu#|~qTk&SS5b)2A z?Y(M$0LZ;&5t=vKQ3h?0J^jO{%*stm5AQkS{|tK9T1v6&lW*(gH6ucjWm@iPtikSG z#lERLz0~w-9SRcHQ&l1rx1a7sa`?c^=GZCIz8fVrn`4g+sX&|2PS?+Dg z%f;Gqg6>Gnnw4g_L!TyHL||3CBt%C6^1^efEq>0m%`}y%m)}O|(GCGzD^oSy>cw6m z%3)F!%twy#n*N}q`sIfSU+kNUAHQSR9A!roH0)i~GVRM}(3{5*aTzqXkvE z&D*gWz^^uHxaBWZJ7E78h&D=eCl+T4`jEw%D@-`365=c9=5n-AA1-Yswc>~L+$P$3 z;BFRjG8@r6{^{EV-v^=0elgX0rW>cSc>)DdeL7zS6HnRJa#4!vJs_T!bOu?8LYst{ zH!Y?U67j>_oKZ;;sLe}QEj$|uK?!u_Y|K3ONuY%r@_fG{@FZq0O?Llr+KaeFw7)WO zZb(KtHoK`5(o!ie%kR8HULVU#T|cfZ>dga!D;cZ0Ys7h4NPyinE)?O%8)C$B!b6=F zM%aBcFs4Rr>VSuB<1jbPQz3y-E*}A0W*^lE0pFF(9+0Shg2a?ryvfOZ$z~1FKnYDy!*Ul#I6-o<@6jqUO&cD(mg}(!be%Ab5J` zP=w7_dx@jk^|S@%&}TDTG^g=U&BN?>mv-@&lW)IQjco1J-am!c|MVO1j|MR870b|A z0Dxq{FU;`LKz>yQU-4f%(%Kqsh#f2V$QRZ|%3MPKWXJv47yu~+sfG9Q_VjZ0_Pyim z1L~|g(SkS$=$ookX)m{-4O}ihjfuHRu2paV~6R|N5IM1|CLa*sI!eUQAun zLj~HidNzM~;JKF&v$x06LoLgasN2M7i;^yQzsI3m%c@17<=gEVz-6O zeX$ccS7fub;UE{71=2&1Y;uN5Q|kB#y9Tq=h2{nG#4ji2oD*MuZ@9 zJ^!D+AW3@&PDa8Bq#*Oo(UrQyza;_ze@eriOMSNwU|w}~87f=$EsWh{UqhD0P8j>Xj}N1=$CQvISy~i^vR0He zrN|741{0B=`A^?}{(H_n&t2a4o^$WH=iaAP|EnGlVM;^G3^*;_&36ic5;R>Wfc>I5 z>0uP8AQU2~&QcgnB}XBSN}0lsRH_v6Qfr>F9ZAi}%yH5{+TvE-DyRW~COQBBjtOE@ z#5A#rp)Ftb7uiVMqY}^p_F^K2+vYdSjTPg!6tHgmumhSyOFXtyJx88Rb+)@ba&r^iyq{i$%Qr~IRh@)5cQ&~aaE z_q=*(B?~QwcssU_TLNAe=A^Hnjdq|<+T?FhO7G1P3G~WxPZao$dy=+J+wbm9N1OwZ z;0q$dLJYQ!F4jkYkq57JZcUezJs&!hT*;|rYo!fxzuX@v6sc&W7y{v;&Yg=Wq)&32 zMn?(1erg6qR}<5i>c_?Z_}olKdbC$iM!Z=Fl%j^&ryEybrmy^Gw&e?1q( znk8Y|ThT18i8Utdw6AK^^LGz6RY*&3^#ToJtXifOUq^}h|KUQo0M|^40KIoGVf&EBm z<{;D?x1jd6ZxP*mcVw2PfMy~1leXHY6r+{}9tS+YPBGhlM(1S#jOa? zL|yLdyy1~`hiH6jm47jT%YFZl{_nMAJiWF^CKRyxZaD)0kdE2^@>b z*=-})#!T83-%{}e#Jioc#W*B=8g^`hHKn}ckzXKjQDwSbG9BIovpdJ1NEdqeexjlB z51-tDCP{>4798UBRV=@HphL{0BltMs zZM7Q5nA@;g9bfs87`os3SzlYfi)pSw_e#s=wumqtSp7V+sB(ZXBHebd74FQ0H5hJ{ z3z~V|sv0%iY9@={N_k<}0~4%sp>-`L2aMXCo40q)Xo@mLNcPac9<;T|4vaaN(a!e3vRJ9&N&2PWDBlh?Df3slC`Rjw>k z*YgFop|400UvR)O#zTt1^B=?x1~_2MmpS*}N`ZS)KNIMDwBL2elCA&gXE|g<&OiIw@#D9(1017wnn(T`+6)|D0TDcN+XHDnwF(nTiJE$Ur9avAQz_Vd zh(|#wTlKPkJ)m0{L$nEgS`jmszpUtjoSFER}RlS$80s{EPXkTa*bb7wF;$Wy`M`(AUQOKn^wR;>$TMice$O zqIlbR0cqh{5A<0DukD*z@X@hFx)e;N3_Jb zlb=7pwpOM_5cQN@mFy5!>@{tQ{w3Rww-v+}prz{?@MadJ1P9TNIHo9L{2fU~8RPk= z(V!I_{kPuzI#R|&KG|3OW^6&9J5$iZWqm@>1O3c$r2tTfWEF_6A8LtvSq3HG^IG`%xLhhxc*R-2??Ad{?h=w-yG?uLJme>wlrg@+RAlk~S;o4X=h0ax~4>j^bh(@7iub@6T$`*(n1FkgT(eeOpu-*@fTR zMh8_g3=XQ@`nC$QZ=ku&cDS76mS6a4L@@|FwHUB0N#zgThe4m~?Q3k@-{chc zI?GSCy05`!#E3;djdcXm9f~6B;S(G`p3bDi>FuD(>DDD9SKpusxk$l_h03X=G#<*S zo=xq(^}F+dk9x^hmeoG)89(_cS$++5s5izHBem@x9O$<{BN5MTSWqo*xHf4~;Kw?^ zF=l`?7vy?+A#E@j5%Y>k%5N6q)cnP59`>UTB|QswtJ)S6&Kj*) zp`1S#_h!jGM9n#)TM%cQkT-~pUrn+=%m~A>bW4MFKS`IGy_+culUXqxpSe3Y^MhC? z14x%*GOQT{lPdJ2wm0z6NzYBUWIK*C_CEL>GsP?>1nBRfGZUWO%$9s&jCKQ?(@7o< znhLjl;90gI7%)k1ow4|$8rh0cQi0keb(?qLNO$F0>St9OC+MHSWYtGf%1s zhMiXnlPd;714mk!lNH$klNzq5Pq*Ey)ub;xLR<=K(_+Uo(r-8$$tR&*zYSflFeAm}{jtXG8k}e0(hBO%E?AND09& zyw?JS>k1)vphaPGM6mJqGE*=9rq=rDR43^A>@mU=NJY&>^RG2y>V#mY+KibO(xPla z2qI6RIYfoRbci~Ic!(~AtipyA+6kLe*dXl6_*a`TAQ4C4Y3F}R6Cq+l2^$j;pwh$; zME)z~%%TF+*Lg4;yrLK*QC5-Dlv9iUTh#wH0PKL{W87)rWlXs+Gi9Y0C%M$TCqtST eK`~BB?*E value.getDeployType().equals(deployType)) + .findFirst() + .orElse(null); } public static boolean isValidDeployType(Integer deployType) { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java index 9aba854b0f..aee2fbe0f6 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java @@ -34,12 +34,10 @@ public enum IotPluginStatusEnum implements IntArrayValuable { } public static IotPluginStatusEnum fromState(Integer state) { - for (IotPluginStatusEnum value : values()) { - if (value.getStatus().equals(state)) { - return value; - } - } - return null; + return Arrays.stream(values()) + .filter(value -> value.getStatus().equals(state)) + .findFirst() + .orElse(null); } @Override diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java index 27368d2688..8b9cef2b46 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java @@ -35,14 +35,11 @@ public enum IotPluginTypeEnum implements IntArrayValuable { return ARRAYS; } - // TODO @haohao:可以使用 hutool 简化 public static IotPluginTypeEnum fromType(Integer type) { - for (IotPluginTypeEnum value : values()) { - if (value.getType().equals(type)) { - return value; - } - } - return null; + return Arrays.stream(values()) + .filter(value -> value.getType().equals(type)) + .findFirst() + .orElse(null); } public static boolean isValidType(Integer type) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java index 4bb5b6e78f..96eb3dda63 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java @@ -1,14 +1,14 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoImportReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoImportReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService; +import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -72,6 +72,7 @@ public class PluginInfoController { @PostMapping("/upload-file") @Operation(summary = "上传插件文件") + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") public CommonResult uploadFile(@Valid PluginInfoImportReqVO reqVO) { pluginInfoService.uploadFile(reqVO.getId(), reqVO.getFile()); return success(true); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInstanceController.java similarity index 76% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInstanceController.java index 9382f8c6d7..dc6433c7f9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInstanceController.java @@ -1,32 +1,31 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance; - -import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import jakarta.validation.*; -import jakarta.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +package cn.iocoder.yudao.module.iot.controller.admin.plugin; import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; -import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService; +import cn.iocoder.yudao.module.iot.service.plugin.PluginInstanceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管理后台 - IoT 插件实例") @RestController @@ -78,17 +77,4 @@ public class PluginInstanceController { return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class)); } - @GetMapping("/export-excel") - @Operation(summary = "导出IoT 插件实例 Excel") - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class, - BeanUtils.toBean(list, PluginInstanceRespVO.class)); - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java similarity index 89% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java index 9216d09ecb..e71e4c484d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoImportReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java new file mode 100644 index 0000000000..7d4677dd49 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") +@Data +public class PluginInfoPageReqVO extends PageParam { + + @Schema(description = "插件名称", example = "http") + private String name; + + @Schema(description = "状态") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java similarity index 96% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java index 6f081764a4..af1ee3e146 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; @@ -34,7 +34,7 @@ public class PluginInfoRespVO { @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("插件包文件名") - private String file; + private String fileName; @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("插件版本") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java index 8ce254a3ae..b35c45abcf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; @@ -23,7 +23,7 @@ public class PluginInfoSaveReqVO { private Integer deployType; @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) - private String file; + private String fileName; @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) private String version; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java index 9fccdc0aec..23f885d5a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstancePageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import lombok.*; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java similarity index 95% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java index 92edca821d..e92b49f1ea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceRespVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java index 95db299d19..802a7f01f2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInstanceSaveReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java deleted file mode 100644 index 321eef9b6f..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java +++ /dev/null @@ -1,116 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo; - -import jakarta.annotation.Resource; -import org.pf4j.spring.SpringPluginManager; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import javax.annotation.security.PermitAll; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 插件 Controller 测试用例 - */ -@RestController -@RequestMapping("/iot/plugins") -public class PluginController { - - @Resource - private SpringPluginManager springPluginManager; - - @Value("${pf4j.pluginsDir}") - private String pluginsDir; - - /** - * 上传插件 JAR 文件并加载插件 - * - * @param file 上传的 JAR 文件 - * @return 上传结果 - */ - @PermitAll - @PostMapping("/upload") - public ResponseEntity uploadPlugin(@RequestParam("file") MultipartFile file) { - if (file.isEmpty()) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件为空"); - } - - // 确保插件目录存在 - Path pluginsPath = Paths.get(pluginsDir); - try { - if (!Files.exists(pluginsPath)) { - Files.createDirectories(pluginsPath); - } - - // 保存上传的 JAR 文件到插件目录 - String filename = file.getOriginalFilename(); - if (filename == null || (!filename.endsWith(".jar") && !filename.endsWith(".zip"))) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件不是 JAR 或 ZIP 文件"); - } - - Path jarPath = pluginsPath.resolve(filename); - - Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); - - // 加载插件 - String pluginId = springPluginManager.loadPlugin(jarPath.toAbsolutePath()); - - // 启动插件 - springPluginManager.startPlugin(pluginId); - - return ResponseEntity.ok("插件上传并加载成功"); - } catch (IOException e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage()); - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage()); - } - } - - /** - * 卸载指定插件 - * - * @param pluginId 插件 ID - * @return 卸载结果 - */ - @PermitAll - @DeleteMapping("/unload/{pluginId}") - public ResponseEntity unloadPlugin(@PathVariable String pluginId) { - if (springPluginManager.getPlugins().stream().noneMatch(plugin -> plugin.getDescriptor().getPluginId().equals(pluginId))) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("插件未加载: " + pluginId); - } - - springPluginManager.stopPlugin(pluginId); - springPluginManager.unloadPlugin(pluginId); - - // 删除插件 JAR 文件(可选) -// PluginWrapper plugin = pluginManager.getPlugin(pluginId); -// PluginDescriptor descriptor = plugin.getDescriptor(); -// Path jarPath = Paths.get(pluginsDir).resolve(descriptor.getPluginId() + ".jar"); -// Files.deleteIfExists(jarPath); - - return ResponseEntity.ok("插件卸载成功: " + pluginId); - } - - /** - * 列出所有已加载的插件 - * - * @return 插件列表 - */ - @PermitAll - @GetMapping("/list") - public ResponseEntity> listPlugins() { - List plugins = springPluginManager.getPlugins().stream() - .map(plugin -> plugin.getDescriptor().getPluginId()) - .collect(Collectors.toList()); - return ResponseEntity.ok(plugins); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java deleted file mode 100644 index a3b36da9df..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; -import lombok.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -// TODO @haohao:只查询必要字段哈 -@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") -@Data -public class PluginInfoPageReqVO extends PageParam { - - @Schema(description = "插件包 ID ", example = "24627") - private String pluginId; - - @Schema(description = "插件名称", example = "赵六") - private String name; - - @Schema(description = "描述", example = "你猜") - private String description; - - @Schema(description = "部署方式", example = "2") - private Integer deployType; - - @Schema(description = "插件包文件名") - private String file; - - @Schema(description = "插件版本") - private String version; - - @Schema(description = "插件类型", example = "2") - @InEnum(IotPluginTypeEnum.class) - private Integer type; - - @Schema(description = "设备插件协议类型") - private String protocol; - - @Schema(description = "状态") - private Integer status; - - @Schema(description = "插件配置项描述信息") - private String configSchema; - - @Schema(description = "插件配置信息") - private String config; - - @Schema(description = "插件脚本") - private String script; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java index 043cdb9c81..c503a830f2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -27,6 +30,7 @@ public class PluginInfoDO extends BaseDO { @TableId private Long id; // TODO @haohao:这个是不是改成类似 key 之类的字段哈? + // 回复:默认是 pluginId,可以不用改 /** * 插件包 ID */ @@ -41,22 +45,23 @@ public class PluginInfoDO extends BaseDO { private String description; /** * 部署方式 + *

+ * 枚举 {@link IotPluginDeployTypeEnum} */ - // TODO @haohao:枚举 private Integer deployType; /** * 插件包文件名 */ - // TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl? - private String file; + private String fileName; /** * 插件版本 */ private String version; /** * 插件类型 + *

+ * 枚举 {@link IotPluginTypeEnum} */ - // TODO @haohao:枚举 private Integer type; /** * 设备插件协议类型 @@ -64,8 +69,9 @@ public class PluginInfoDO extends BaseDO { private String protocol; /** * 状态 + *

+ * 枚举 {@link IotPluginStatusEnum} */ - // TODO @haohao:枚举 private Integer status; /** * 插件配置项描述信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java new file mode 100644 index 0000000000..228519fb65 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInfoMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.*; + +/** + * IoT 插件信息 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface PluginInfoMapper extends BaseMapperX { + + default PageResult selectPage(PluginInfoPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) + .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) + .orderByDesc(PluginInfoDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java similarity index 89% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java index 6c7f1d231c..7eee95509e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance; +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*; /** * IoT 插件实例 Mapper diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java deleted file mode 100644 index 69c0bd3929..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*; - -/** - * IoT 插件信息 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface PluginInfoMapper extends BaseMapperX { - - default PageResult selectPage(PluginInfoPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId()) - .likeIfPresent(PluginInfoDO::getName, reqVO.getName()) - .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription()) - .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType()) - .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile()) - .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion()) - .eqIfPresent(PluginInfoDO::getType, reqVO.getType()) - .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol()) - .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus()) - .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema()) - .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig()) - .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript()) - .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(PluginInfoDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java similarity index 87% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java index d023791c81..40ed4a156f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.service.plugininfo; +package cn.iocoder.yudao.module.iot.service.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java similarity index 95% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java index 333d0fa988..d9a9eaaff7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.service.plugininfo; +package cn.iocoder.yudao.module.iot.service.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -41,7 +41,6 @@ public class PluginInfoServiceImpl implements PluginInfoService { @Resource private PluginInfoMapper pluginInfoMapper; - @Resource private SpringPluginManager pluginManager; @@ -94,12 +93,12 @@ public class PluginInfoServiceImpl implements PluginInfoService { Executors.newSingleThreadExecutor().submit(() -> { try { TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 - File file = new File(pluginsDir, pluginInfoDO.getFile()); + File file = new File(pluginsDir, pluginInfoDO.getFileName()); if (file.exists() && !file.delete()) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFile()); + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); } } catch (InterruptedException e) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFile(), e); + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); } }); @@ -213,7 +212,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginIdNew, MultipartFile file) { pluginInfoDo.setPluginId(pluginIdNew); pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); - pluginInfoDo.setFile(file.getOriginalFilename()); + pluginInfoDo.setFileName(file.getOriginalFilename()); pluginInfoDo.setScript(""); PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginIdNew).getDescriptor(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java index 0789c46381..97346f9b73 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.service.plugininstance; +package cn.iocoder.yudao.module.iot.service.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; import jakarta.validation.Valid; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java similarity index 83% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index afb05ef2f8..7b26948d0a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -1,11 +1,11 @@ -package cn.iocoder.yudao.module.iot.service.plugininstance; +package cn.iocoder.yudao.module.iot.service.plugin; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper; +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -13,7 +13,6 @@ import org.springframework.validation.annotation.Validated; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS; -// TODO @haohao:可以搞个 plugin 包,然后把 plugininfo、plugininstance /** * IoT 插件实例 Service 实现类 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml index 2d297c785b..007a417ede 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml @@ -1,6 +1,6 @@ - + - CREATE STABLE ${dataBaseName}.${superTableName}( + CREATE STABLE thing_model_message_${superTableName}( ts TIMESTAMP, - id VARCHAR(255), - sys VARCHAR(2048), - method VARCHAR(255), - params VARCHAR(2048) + id NCHAR(64), + sys NCHAR(2048), + method NCHAR(255), + params NCHAR(2048) )TAGS ( - device_key VARCHAR(255) + device_key NCHAR(50) ) - CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} - USING ${dataBaseName}.${superTableName}( + CREATE STABLE ${tableName} + USING thing_model_message_${superTableName}( ts, id , sys , From eaee4642d628ecfb146ac5a1e1826a3c9cbaad6a Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Tue, 31 Dec 2024 17:00:25 +0800 Subject: [PATCH 067/309] =?UTF-8?q?[=E5=8A=9F=E8=83=BD=E6=B7=BB=E5=8A=A0]?= =?UTF-8?q?=EF=BC=9A=E7=89=A9=E6=A8=A1=E5=9E=8B=E6=97=A5=E5=BF=97=E8=A1=A8?= =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tdengine/ThingModelMessageDO.java | 24 ++++--- .../tdengine/TdThingModelMessageMapper.java | 6 +- .../IotDevicePropertyDataServiceImpl.java | 7 ++ .../product/IotProductServiceImpl.java | 6 +- .../tdengine/IotThingModelMessageService.java | 7 +- .../IotThingModelMessageServiceImpl.java | 64 +++++++++---------- .../mapper/tdengine/TdEngineDMLMapper.xml | 2 +- .../tdengine/TdThinkModelMessageMapper.xml | 12 ++-- 8 files changed, 62 insertions(+), 66 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java index 7dca6edb06..b647c68730 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; // TODO @芋艿:纠结下字段 +@Deprecated /** * TD 物模型消息日志的数据库 */ @@ -17,26 +18,17 @@ public class ThingModelMessageDO { - // TODO @haohao:superTableName 和 tableName 是不是合并。因为每个 mapper 操作的时候,有且只会使用到其中一个。 - /** - * 超级表名称 - */ - private String superTableName; - - /** - * 表名称 - */ - private String tableName; - /** * 消息 ID */ private String id; /** - * 扩展功能的参数 + * 系统扩展参数 + * + * 例如:设备状态、系统时间、固件版本等系统级信息 */ - private Object sys; + private Object system; /** * 请求方法 @@ -55,6 +47,12 @@ public class ThingModelMessageDO { */ private Long time; + /** + * 设备信息 + */ + private String productKey; + + /** * 设备 key */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java index fc66119315..798d084ac9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java @@ -1,11 +1,13 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessageDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; /** * 处理 TD 中物模型消息日志的操作 @@ -20,13 +22,13 @@ public interface TdThingModelMessageMapper { * */ - void createSuperTable(ThingModelMessageDO superTable); + void createSuperTable(@Param("productKey") String productKey); /** * 创建子表 * */ - void createTableWithTag(ThingModelMessageDO table); + void createTableWithTag(@Param("productKey") String productKey,@Param("deviceKey") String deviceKey); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index eb7fcd430c..cb74747745 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; @@ -84,6 +85,9 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe @Resource private IotDevicePropertyDataMapper devicePropertyDataMapper; + @Resource + private TdThingModelMessageMapper tdThingModelMessageMapper; + @Override public void defineDevicePropertyData(Long productId) { // 1.1 查询产品和物模型 @@ -108,7 +112,10 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe return; } newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); + // 2.1.1 创建产品超级表 devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); + // 2.1.2 创建物模型日志超级表 + tdThingModelMessageMapper.createSuperTable(product.getProductKey()); return; } // 2.2 情况二:如果是修改的时候,需要更新表 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index d384e44c04..41537664b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -34,9 +34,6 @@ public class IotProductServiceImpl implements IotProductService { @Resource private IotProductMapper productMapper; - @Resource - @Lazy // 延迟加载,解决循环依赖 - private IotThingModelMessageService thingModelMessageService; @Resource @Lazy // 延迟加载,解决循环依赖 private IotDevicePropertyDataService devicePropertyDataService; @@ -125,8 +122,7 @@ public class IotProductServiceImpl implements IotProductService { if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { // 3.1 创建产品超级表数据模型 devicePropertyDataService.defineDevicePropertyData(id); - // 3.2 创建物模型日志超级表数据模型 TODO 待定:message 要不要分; - thingModelMessageService.createSuperTable(id); + } productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java index ab77eb91ac..7f52411d89 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java @@ -16,11 +16,6 @@ public interface IotThingModelMessageService { */ void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); - /** - * 创建物模型消息日志超级表 - * - * @param productId 产品编号 - */ - void createSuperTable(Long productId); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 1654c56f5e..395d1e91f1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -79,7 +79,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); // 1.2 创建物模型日志设备表 - createThingModelMessageDeviceTable(device.getProductKey(), device.getDeviceKey()); + tdThingModelMessageMapper.createTableWithTag(device.getProductKey(), device.getDeviceKey()); } // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 @@ -108,32 +108,34 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); } - @Override - @TenantIgnore - public void createSuperTable(Long productId) { - // 1. 查询产品 - IotProductDO product = productService.getProduct(productId); - - // 2. 获取超级表的名称和数据库名称 - // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 - String databaseName = IotTdDatabaseUtils.getDatabaseName(url); - String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey()); - - // 解析物模型,获取字段列表 - List schemaFields = List.of( - TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(), - TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(), - TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(), - TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(), - TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build() - ); - // 设置超级表的标签 - List tagsFields = List.of( - TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build() - ); - // 3. 创建超级表 - tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); - } +// @Override +// @TenantIgnore +// public void createSuperTable(Long productId) { +// // 1. 查询产品 +// IotProductDO product = productService.getProduct(productId); +// // 2. 创建日志超级表 +// tdThingModelMessageMapper.createSuperTable(product.getProductKey()); +// +// // 2. 获取超级表的名称和数据库名称 +// // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 +//// String databaseName = IotTdDatabaseUtils.getDatabaseName(url); +//// String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey()); +//// +//// // 解析物模型,获取字段列表 +//// List schemaFields = List.of( +//// TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(), +//// TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(), +//// TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(), +//// TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(), +//// TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build() +//// ); +//// // 设置超级表的标签 +//// List tagsFields = List.of( +//// TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build() +//// ); +//// // 3. 创建超级表 +//// tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); +// } private List getValidFunctionList(String productKey) { return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), @@ -237,13 +239,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ * */ private void createThingModelMessageDeviceTable(String productKey, String deviceKey){ - - - // 2. 创建物模型日志设备数据表 -// tdThingModelMessageMapper.createTableWithTag(ThingModelMessageDO.builder().build() -// .setSuperTableName(productKey) -// .setTableName(deviceKey) -// .setDeviceKey(deviceKey)); + tdThingModelMessageMapper.createTableWithTag(productKey, deviceKey); } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml index 0443a826b2..b4084374cf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml @@ -42,7 +42,7 @@ + SELECT ts, id, device_key, product_key, type, subType, content, report_time + FROM device_log_${reqVO.deviceKey} + + + AND type = #{reqVO.type} + + + AND subType = #{reqVO.subType} + + + AND ts BETWEEN #{reqVO.createTime[0]} AND #{reqVO.createTime[1]} + + + ORDER BY ts DESC + LIMIT #{reqVO.pageSize} OFFSET #{reqVO.pageNo} + + + + \ No newline at end of file From 603649d2485ec36f30b77ae2a17957fb4bf28100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 6 Jan 2025 18:59:26 +0800 Subject: [PATCH 074/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=96=B0=E5=A2=9E=20MQTT=20RPC=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E5=8C=85=E5=90=AB=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E5=92=8C=E5=93=8D=E5=BA=94=E6=A8=A1=E5=9E=8B=E3=80=81=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E5=B7=A5=E5=85=B7=E3=80=81MQTT=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=8F=8A=E5=AE=A2=E6=88=B7=E7=AB=AF/=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E5=AE=9E=E7=8E=B0=EF=BC=8C=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=E6=9C=8D=E5=8A=A1=E5=92=8C=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8=E6=8E=A5=E5=8F=A3=EF=BC=8C=E4=BC=98=E5=8C=96=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=BB=93=E6=9E=84=E4=BB=A5=E6=94=AF=E6=8C=81=20HTTP?= =?UTF-8?q?=20=E6=8F=92=E4=BB=B6=E7=9A=84=E9=9B=86=E6=88=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/mqttrpc/common/RpcRequest.java | 37 ++++++++ .../iot/mqttrpc/common/RpcResponse.java | 32 +++++++ .../mqttrpc/common/SerializationUtils.java | 18 ++++ .../module/iot/mqttrpc/config/MqttConfig.java | 40 ++++++++ .../module/iot/mqttrpc/server/RpcServer.java | 95 +++++++++++++++++++ .../iot/service/plugin/ExampleService.java | 43 +++++++++ .../service/plugin/PluginInfoServiceImpl.java | 1 - .../yudao-module-iot-http-plugin/pom.xml | 5 + .../iot/HttpPluginSpringbootApplication.java | 11 +++ .../module/iot/controller/RpcController.java | 31 ++++++ .../module/iot/mqttrpc/client/RpcClient.java | 95 +++++++++++++++++++ .../module/iot/mqttrpc/config/MqttConfig.java | 41 ++++++++ .../src/main/resources/application.yml | 15 +++ .../yudao-module-iot-mqtt-plugin/pom.xml | 6 +- .../yudao/module/iot/plugin/MqttPlugin.java | 13 +-- 15 files changed, 471 insertions(+), 12 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java new file mode 100644 index 0000000000..14e84175c0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * MQTT RPC 请求 + * + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RpcRequest { + + /** + * 方法名 + */ + private String method; + + /** + * 参数 + */ + private Object[] params; + + /** + * 关联 ID + */ + private String correlationId; + + /** + * 回复地址 + */ + private String replyTo; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java new file mode 100644 index 0000000000..675a6ee71b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.common; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * MQTT RPC 响应 + * + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RpcResponse { + + /** + * 关联 ID + */ + private String correlationId; + + /** + * 结果 + */ + private Object result; + + /** + * 错误 + */ + private String error; +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java new file mode 100644 index 0000000000..1529e2dba1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.common; + +import cn.hutool.json.JSONUtil; + +/** + * 序列化工具类 + * + */ +public class SerializationUtils { + + public static String serialize(Object obj) { + return JSONUtil.toJsonStr(obj); + } + + public static T deserialize(String json, Class clazz) { + return JSONUtil.toBean(json, clazz); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java new file mode 100644 index 0000000000..c7a0500030 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "mqtt") +public class MqttConfig { + /** + * MQTT 代理地址 + */ + private String broker; + + /** + * MQTT 用户名 + */ + private String username; + + /** + * MQTT 密码 + */ + private String password; + + /** + * MQTT 客户端 ID + */ + private String clientId; + + /** + * MQTT 请求主题 + */ + private String requestTopic; + + /** + * MQTT 响应主题前缀 + */ + private String responseTopicPrefix; +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java new file mode 100644 index 0000000000..be6ca6f831 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.server; + +import cn.hutool.core.lang.UUID; +import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcRequest; +import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcResponse; +import cn.iocoder.yudao.module.iot.mqttrpc.common.SerializationUtils; +import cn.iocoder.yudao.module.iot.mqttrpc.config.MqttConfig; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.HashMap; +import java.util.Map; + +@Service +@Slf4j +public class RpcServer { + + private final MqttConfig mqttConfig; + private final MqttClient mqttClient; + private final Map methodRegistry = new HashMap<>(); + + public RpcServer(MqttConfig mqttConfig) throws MqttException { + this.mqttConfig = mqttConfig; + this.mqttClient = new MqttClient(mqttConfig.getBroker(), "rpc-server-" + UUID.randomUUID(), new MemoryPersistence()); + MqttConnectOptions options = new MqttConnectOptions(); + options.setAutomaticReconnect(true); + options.setCleanSession(true); + options.setUserName(mqttConfig.getUsername()); + options.setPassword(mqttConfig.getPassword().toCharArray()); + this.mqttClient.connect(options); + } + + @PostConstruct + public void init() throws MqttException { + mqttClient.subscribe(mqttConfig.getRequestTopic(), this::handleRequest); + log.info("RPC Server subscribed to topic: {}", mqttConfig.getRequestTopic()); + } + + private void handleRequest(String topic, MqttMessage message) { + RpcRequest request = SerializationUtils.deserialize(new String(message.getPayload()), RpcRequest.class); + RpcResponse response = new RpcResponse(); + response.setCorrelationId(request.getCorrelationId()); + + try { + MethodInvoker invoker = methodRegistry.get(request.getMethod()); + if (invoker == null) { + throw new NoSuchMethodException("Unknown method: " + request.getMethod()); + } + Object result = invoker.invoke(request.getParams()); + response.setResult(result); + } catch (Exception e) { + response.setError(e.getMessage()); + log.error("Error processing RPC request: {}", e.getMessage(), e); + } + + String replyPayload = SerializationUtils.serialize(response); + MqttMessage replyMessage = new MqttMessage(replyPayload.getBytes()); + replyMessage.setQos(1); + try { + mqttClient.publish(request.getReplyTo(), replyMessage); + log.info("Published response to {}", request.getReplyTo()); + } catch (MqttException e) { + log.error("Failed to publish response: {}", e.getMessage(), e); + } + } + + /** + * 注册可调用的方法 + * + * @param methodName 方法名称 + * @param invoker 方法调用器 + */ + public void registerMethod(String methodName, MethodInvoker invoker) { + methodRegistry.put(methodName, invoker); + log.info("Registered method: {}", methodName); + } + + @PreDestroy + public void cleanup() throws MqttException { + mqttClient.disconnect(); + log.info("RPC Server disconnected"); + } + + /** + * 方法调用器接口 + */ + @FunctionalInterface + public interface MethodInvoker { + Object invoke(Object[] params) throws Exception; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java new file mode 100644 index 0000000000..22ebe8b4f2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.iot.service.plugin; + +import cn.iocoder.yudao.module.iot.mqttrpc.server.RpcServer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +@Service +@RequiredArgsConstructor +public class ExampleService { + + private final RpcServer rpcServer; + + @PostConstruct + public void registerMethods() { + rpcServer.registerMethod("add", params -> { + if (params.length != 2) { + throw new IllegalArgumentException("add方法需要两个参数"); + } + int a = ((Number) params[0]).intValue(); + int b = ((Number) params[1]).intValue(); + return add(a, b); + }); + + rpcServer.registerMethod("concat", params -> { + if (params.length != 2) { + throw new IllegalArgumentException("concat方法需要两个参数"); + } + String str1 = params[0].toString(); + String str2 = params[1].toString(); + return concat(str1, str2); + }); + } + + private int add(int a, int b) { + return a + b; + } + + private String concat(String a, String b) { + return a + b; + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java index 7c856447bc..77cf590a0c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -18,7 +18,6 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import org.springframework.web.multipart.MultipartFile; -import javax.annotation.PostConstruct; import java.io.File; import java.io.IOException; import java.nio.file.*; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index 1ecf140a47..27c1d19a0a 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -150,5 +150,10 @@ netty-all 4.1.63.Final + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java new file mode 100644 index 0000000000..6b553f92bf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java @@ -0,0 +1,11 @@ +package cn.iocoder.yudao.module.iot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HttpPluginSpringbootApplication { + public static void main(String[] args) { + SpringApplication.run(HttpPluginSpringbootApplication.class, args); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java new file mode 100644 index 0000000000..a5175a7862 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java @@ -0,0 +1,31 @@ + +package cn.iocoder.yudao.module.iot.controller; + +import cn.iocoder.yudao.module.iot.mqttrpc.client.RpcClient; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.concurrent.CompletableFuture; + +@RestController +@RequestMapping("/rpc") +@RequiredArgsConstructor +public class RpcController { + + @Resource + private RpcClient rpcClient; + + @PostMapping("/add") + public CompletableFuture add(@RequestParam int a, @RequestParam int b) throws Exception { + return rpcClient.call("add", new Object[]{a, b}, 10); + } + + @PostMapping("/concat") + public CompletableFuture concat(@RequestParam String str1, @RequestParam String str2) throws Exception { + return rpcClient.call("concat", new Object[]{str1, str2}, 10); + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java new file mode 100644 index 0000000000..73c1d936ce --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.client; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcRequest; +import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcResponse; +import cn.iocoder.yudao.module.iot.mqttrpc.common.SerializationUtils; +import cn.iocoder.yudao.module.iot.mqttrpc.config.MqttConfig; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.UUID; +import java.util.concurrent.*; + +@Service +@Slf4j +public class RpcClient { + + private final MqttConfig mqttConfig; + private final MqttClient mqttClient; + private final ConcurrentMap> pendingRequests = new ConcurrentHashMap<>(); + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + + public RpcClient(MqttConfig mqttConfig) throws MqttException { + this.mqttConfig = mqttConfig; + this.mqttClient = new MqttClient(mqttConfig.getBroker(), mqttConfig.getClientId(), new MemoryPersistence()); + MqttConnectOptions options = new MqttConnectOptions(); + options.setAutomaticReconnect(true); + options.setCleanSession(true); + options.setUserName(mqttConfig.getUsername()); + options.setPassword(mqttConfig.getPassword().toCharArray()); + this.mqttClient.connect(options); + } + + @PostConstruct + public void init() throws MqttException { + mqttClient.subscribe(mqttConfig.getResponseTopicPrefix() + "#", this::handleResponse); + log.info("RPC Client subscribed to topics: {}", mqttConfig.getResponseTopicPrefix() + "#"); + } + + private void handleResponse(String topic, MqttMessage message) { + String correlationId = topic.substring(mqttConfig.getResponseTopicPrefix().length()); + RpcResponse response = SerializationUtils.deserialize(new String(message.getPayload()), RpcResponse.class); + CompletableFuture future = pendingRequests.remove(correlationId); + if (future != null) { + if (response.getError() != null) { + future.completeExceptionally(new RuntimeException(response.getError())); + } else { + future.complete(response); + } + } else { + log.warn("Received response for unknown correlationId: {}", correlationId); + } + } + + public CompletableFuture call(String method, Object[] params, int timeoutSeconds) throws MqttException { + String correlationId = UUID.randomUUID().toString(); + String replyTo = mqttConfig.getResponseTopicPrefix() + correlationId; + + RpcRequest request = new RpcRequest(method, params, correlationId, replyTo); + String payload = SerializationUtils.serialize(request); + MqttMessage message = new MqttMessage(payload.getBytes()); + message.setQos(1); + mqttClient.publish(mqttConfig.getRequestTopic(), message); + + CompletableFuture futureResponse = new CompletableFuture<>(); + pendingRequests.put(correlationId, futureResponse); + + // 设置超时 + scheduler.schedule(() -> { + CompletableFuture removed = pendingRequests.remove(correlationId); + if (removed != null) { + removed.completeExceptionally(new TimeoutException("RPC call timed out")); + } + }, timeoutSeconds, TimeUnit.SECONDS); + + // 返回最终的结果 + return futureResponse.thenApply(RpcResponse::getResult); + } + + @PreDestroy + public void cleanup() throws MqttException { + mqttClient.disconnect(); + scheduler.shutdown(); + log.info("RPC Client disconnected"); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java new file mode 100644 index 0000000000..89569b0c3d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.iot.mqttrpc.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Data +@Configuration +@ConfigurationProperties(prefix = "mqtt") +public class MqttConfig { + + /** + * MQTT 代理地址 + */ + private String broker; + + /** + * MQTT 用户名 + */ + private String username; + + /** + * MQTT 密码 + */ + private String password; + + /** + * MQTT 客户端 ID + */ + private String clientId; + + /** + * MQTT 请求主题 + */ + private String requestTopic; + + /** + * MQTT 响应主题前缀 + */ + private String responseTopicPrefix; +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml new file mode 100644 index 0000000000..ea2234f83e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: 8092 + +spring: + application: + name: yudao-module-iot-http-plugin + +# MQTT-RPC 配置 +mqtt: + broker: tcp://chaojiniu.top:1883 + username: haohao + password: ahh@123456 + clientId: mqtt-rpc-client-${random.int} + requestTopic: rpc/request + responseTopicPrefix: rpc/response/ diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml index d5d48d09a4..9607e0f93c 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml @@ -145,10 +145,10 @@ ${lombok.version} provided + - io.netty - netty-all - 4.1.63.Final + org.eclipse.paho + org.eclipse.paho.client.mqttv3 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java index 5b50c71241..b3749e4025 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java @@ -1,11 +1,12 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import lombok.extern.slf4j.Slf4j; -import org.pf4j.PluginWrapper; import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import javax.annotation.Resource; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -13,11 +14,11 @@ import java.util.concurrent.Executors; public class MqttPlugin extends Plugin { private ExecutorService executorService; + @Resource private DeviceDataApi deviceDataApi; public MqttPlugin(PluginWrapper wrapper) { super(wrapper); - // 初始化线程池 this.executorService = Executors.newSingleThreadExecutor(); } @@ -25,24 +26,20 @@ public class MqttPlugin extends Plugin { public void start() { log.info("MqttPlugin.start()"); - // 重新初始化线程池,确保它是活跃的 if (executorService.isShutdown() || executorService.isTerminated()) { executorService = Executors.newSingleThreadExecutor(); } - // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); if (deviceDataApi == null) { log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); return; } + } @Override public void stop() { log.info("MqttPlugin.stop()"); - // 停止线程池 - executorService.shutdownNow(); } - } \ No newline at end of file From b5856c4cfc3a55e89928cb313670210af64a5f7c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 6 Jan 2025 20:24:47 +0800 Subject: [PATCH 075/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=8F=92=E4=BB=B6=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/api/ServiceRegistry.java | 1 + .../module/iot/api/device/DeviceDataApi.java | 3 + .../module/iot/mqttrpc/common/RpcRequest.java | 4 +- .../iot/mqttrpc/common/RpcResponse.java | 3 +- .../mqttrpc/common/SerializationUtils.java | 1 + .../plugin/vo/PluginInfoImportReqVO.java | 2 +- .../admin/plugin/vo/PluginInfoRespVO.java | 17 ----- .../admin/plugin/vo/PluginInfoSaveReqVO.java | 4 + .../mysql/plugin/PluginInstanceMapper.java | 1 + .../iot/job/plugin/PluginInstancesJob.java | 2 +- .../module/iot/mqttrpc/server/RpcServer.java | 5 ++ .../service/plugin/PluginInfoServiceImpl.java | 76 ++++++++++--------- .../plugin/PluginInstanceServiceImpl.java | 29 +++---- .../module/iot/controller/RpcController.java | 1 + .../module/iot/mqttrpc/client/RpcClient.java | 12 ++- 15 files changed, 86 insertions(+), 75 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java index 5603ad8d72..a914e8029f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.api; import java.util.HashMap; import java.util.Map; +// TODO 芋艿:纠结下 /** * 服务注册表 - 插架模块使用,无法使用 Spring 注入 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java index cb747f5053..076064db82 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java @@ -2,9 +2,12 @@ package cn.iocoder.yudao.module.iot.api.device; /** * 设备数据 API + * + * @author haohao */ public interface DeviceDataApi { + // TODO @haohao:最好搞成 dto 哈! /** * 保存设备数据 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java index 14e84175c0..b2a9f03607 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java @@ -5,9 +5,9 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +// TODO @芋艿:要不要加个 mqtt 值了的前缀 /** * MQTT RPC 请求 - * */ @Data @Builder @@ -23,6 +23,7 @@ public class RpcRequest { /** * 参数 */ + // TODO @haohao:object 对象会不会不好序列化? private Object[] params; /** @@ -34,4 +35,5 @@ public class RpcRequest { * 回复地址 */ private String replyTo; + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java index 675a6ee71b..f3225d08e7 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java @@ -7,7 +7,6 @@ import lombok.NoArgsConstructor; /** * MQTT RPC 响应 - * */ @Data @Builder @@ -23,10 +22,12 @@ public class RpcResponse { /** * 结果 */ + // TODO @haohao:object 对象会不会不好反序列化? private Object result; /** * 错误 */ private String error; + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java index 1529e2dba1..620b007635 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java @@ -15,4 +15,5 @@ public class SerializationUtils { public static T deserialize(String json, Class clazz) { return JSONUtil.toBean(json, clazz); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java index e71e4c484d..bc8d6c8fae 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoImportReqVO.java @@ -9,7 +9,7 @@ import org.springframework.web.multipart.MultipartFile; @Data public class PluginInfoImportReqVO { - @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") private Long id; @Schema(description = "插件文件", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java index 514ba4f1f1..4291024699 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java @@ -1,7 +1,5 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -9,63 +7,48 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 插件信息 Response VO") @Data -@ExcelIgnoreUnannotated public class PluginInfoRespVO { @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") - @ExcelProperty("主键 ID") private Long id; @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") - @ExcelProperty("插件包标识符") private String pluginKey; @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @ExcelProperty("插件名称") private String name; @Schema(description = "描述", example = "你猜") - @ExcelProperty("描述") private String description; @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("部署方式") private Integer deployType; @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("插件包文件名") private String fileName; @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("插件版本") private String version; @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty("插件类型") private Integer type; @Schema(description = "设备插件协议类型") - @ExcelProperty("设备插件协议类型") private String protocol; @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("状态") private Integer status; @Schema(description = "插件配置项描述信息") - @ExcelProperty("插件配置项描述信息") private String configSchema; @Schema(description = "插件配置信息") - @ExcelProperty("插件配置信息") private String config; @Schema(description = "插件脚本") - @ExcelProperty("插件脚本") private String script; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") private LocalDateTime createTime; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java index 9a98481306..ad3b31fc1c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java @@ -7,6 +7,10 @@ import lombok.*; @Data public class PluginInfoSaveReqVO { + // TODO @haohao:新增的字段有点多,每个都需要哇? + + // TODO @haohao:一些枚举字段,需要加枚举校验。例如说,deployType、status、type 等 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") private Long id; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java index 249082032d..4f773aa064 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java @@ -21,6 +21,7 @@ public interface PluginInstanceMapper extends BaseMapperX { .eq(PluginInstanceDO::getPluginId, pluginId)); } + // TODO @haohao:这个还需要么?相关不用的 VO 可以删除 default PageResult selectPage(PluginInstancePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId()) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java index ca8398e51c..47e7bf5605 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.job.plugin; - import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.service.plugin.PluginInstanceService; import org.springframework.scheduling.annotation.Scheduled; @@ -26,4 +25,5 @@ public class PluginInstancesJob { pluginInstanceService.updatePluginInstances(); }); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java index be6ca6f831..90ce2a3875 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java @@ -15,6 +15,8 @@ import javax.annotation.PreDestroy; import java.util.HashMap; import java.util.Map; +// TODO @芋艿:server 逻辑,再瞅瞅; +// TODO @haohao:如果只写在 iot biz 里,那么后续 server => client 貌似不方便?微信再讨论下~; @Service @Slf4j public class RpcServer { @@ -90,6 +92,9 @@ public class RpcServer { */ @FunctionalInterface public interface MethodInvoker { + Object invoke(Object[] params) throws Exception; + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java index 77cf590a0c..c9030b9244 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -41,18 +41,18 @@ public class PluginInfoServiceImpl implements PluginInfoService { @Resource private PluginInfoMapper pluginInfoMapper; + @Resource private SpringPluginManager pluginManager; + // TODO @芋艿:要不要换位置 @Value("${pf4j.pluginsDir}") private String pluginsDir; @Override public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { - // 插入 PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class); pluginInfoMapper.insert(pluginInfo); - // 返回 return pluginInfo.getId(); } @@ -67,29 +67,29 @@ public class PluginInfoServiceImpl implements PluginInfoService { @Override public void deletePluginInfo(Long id) { - // 校验存在 + // 1.1 校验存在 PluginInfoDO pluginInfoDO = validatePluginInfoExists(id); - - // 停止插件 + // 1.2 停止插件 if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING); } - // 卸载插件 + // 2. 卸载插件 + // TODO @haohao:可以复用 stopAndUnloadPlugin PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginKey()); if (plugin != null) { - // 查询插件是否是启动状态 + // 停止插件 if (plugin.getPluginState().equals(PluginState.STARTED)) { - // 停止插件 pluginManager.stopPlugin(plugin.getPluginId()); } // 卸载插件 pluginManager.unloadPlugin(plugin.getPluginId()); } - // 删除 + // 3.1 删除 pluginInfoMapper.deleteById(id); - // 删除插件文件 + // 3.2 删除插件文件 + // TODO @haohao:这个直接主线程 sleep 就好了,不用单独开线程池哈。原因是,低频操作;另外,只有存在的时候,才 sleep + 删除; Executors.newSingleThreadExecutor().submit(() -> { try { TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 @@ -101,7 +101,6 @@ public class PluginInfoServiceImpl implements PluginInfoService { log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); } }); - } private PluginInfoDO validatePluginInfoExists(Long id) { @@ -127,22 +126,19 @@ public class PluginInfoServiceImpl implements PluginInfoService { // 1. 校验插件信息是否存在 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - // 2. 获取插件标识 - String pluginKey = pluginInfoDo.getPluginKey(); + // 2. 停止并卸载旧的插件 + stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); - // 3. 停止并卸载旧的插件 - stopAndUnloadPlugin(pluginKey); - - // 4. 上传新的插件文件 + // 3.1 上传新的插件文件 String pluginKeyNew = uploadAndLoadNewPlugin(file); - - // 5. 更新插件启用状态文件 + // 3.2 更新插件启用状态文件 updatePluginStatusFile(pluginKeyNew, false); - // 6. 更新插件信息 + // 4. 更新插件信息 updatePluginInfo(pluginInfoDo, pluginKeyNew, file); } + // TODO @haohao:注释的格式 // 停止并卸载旧的插件 private void stopAndUnloadPlugin(String pluginKey) { PluginWrapper plugin = pluginManager.getPlugin(pluginKey); @@ -154,10 +150,13 @@ public class PluginInfoServiceImpl implements PluginInfoService { } } + // TODO @haohao:注释的格式 // 上传并加载新的插件文件 private String uploadAndLoadNewPlugin(MultipartFile file) { + // TODO @haohao:多节点,是不是要上传 s3 之类的存储器;然后定时去加载 Path pluginsPath = Paths.get(pluginsDir); try { + // TODO @haohao:可以使用 FileUtil 简化? if (!Files.exists(pluginsPath)) { Files.createDirectories(pluginsPath); // 创建插件目录 } @@ -166,16 +165,18 @@ public class PluginInfoServiceImpl implements PluginInfoService { Path jarPath = pluginsPath.resolve(filename); Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 return pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 - } else { - throw exception(PLUGIN_INSTALL_FAILED); } + throw exception(PLUGIN_INSTALL_FAILED); // TODO @haohao:这么抛的话,貌似会被 catch (Exception e) { } catch (Exception e) { + // TODO @haohao:打个 error log,方便排查 throw exception(PLUGIN_INSTALL_FAILED); } } + // TODO @haohao:注释的格式 // 更新插件状态文件 private void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) { + // TODO @haohao:疑问,这里写 enabled.txt 和 disabled.txt 的目的是啥哈? Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; @@ -186,10 +187,8 @@ public class PluginInfoServiceImpl implements PluginInfoService { if (pluginWrapper == null) { throw exception(PLUGIN_INSTALL_FAILED); } - List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) - : new ArrayList<>(); - List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) - : new ArrayList<>(); + List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) : new ArrayList<>(); + List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) : new ArrayList<>(); if (!targetLines.contains(pluginKeyNew)) { targetLines.add(pluginKeyNew); @@ -207,26 +206,33 @@ public class PluginInfoServiceImpl implements PluginInfoService { } } + // TODO @haohao:注释的格式 // 更新插件信息 private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) { + // TODO @haohao:更新实体的时候,最好 new 一个新的! + // TODO @haohao:可以链式调用,简化下代码; pluginInfoDo.setPluginKey(pluginKeyNew); pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); pluginInfoDo.setFileName(file.getOriginalFilename()); pluginInfoDo.setScript(""); - + // 解析 pf4j 插件 PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginKeyNew).getDescriptor(); pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription()); pluginInfoDo.setVersion(pluginDescriptor.getVersion()); pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); + + // 执行更新 pluginInfoMapper.updateById(pluginInfoDo); } + // TODO @haohao:status、state 字段命名,要统一下~ @Override public void updatePluginStatus(Long id, Integer status) { // 1. 校验插件信息是否存在 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); // 2. 校验插件状态是否有效 + // TODO @haohao:直接参数校验掉。通过 @InEnum if (!IotPluginStatusEnum.contains(status)) { throw exception(PLUGIN_STATUS_INVALID); } @@ -237,17 +243,16 @@ public class PluginInfoServiceImpl implements PluginInfoService { // 4. 根据状态更新插件 if (plugin != null) { - // 4.1 如果目标状态是运行且插件未启动,则启动插件 + // 4.1 启动:如果目标状态是运行且插件未启动,则启动插件 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { pluginManager.startPlugin(pluginKey); - updatePluginStatusFile(pluginKey, true); // 更新插件状态文件为启用 - } - // 4.2 如果目标状态是停止且插件已启动,则停止插件 - else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) + updatePluginStatusFile(pluginKey, true); + // 4.2 停止:如果目标状态是停止且插件已启动,则停止插件 + } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { pluginManager.stopPlugin(pluginKey); - updatePluginStatusFile(pluginKey, false); // 更新插件状态文件为禁用 + updatePluginStatusFile(pluginKey, false); } } else { // 5. 插件不存在且状态为停止,抛出异常 @@ -257,17 +262,20 @@ public class PluginInfoServiceImpl implements PluginInfoService { } // 6. 更新数据库中的插件状态 + // TODO @haohao:新建新建 pluginInfoDo 哈! pluginInfoDo.setStatus(status); pluginInfoMapper.updateById(pluginInfoDo); } @Override public List getPluginInfoList() { - return pluginInfoMapper.selectList(null); + return pluginInfoMapper.selectList(); } + // TODO @haohao:可以改成 getPluginInfoListByStatus 更通用哈。 @Override public List getRunningPluginInfoList() { return pluginInfoMapper.selectListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index 52d79207b8..6a65fc0265 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -26,8 +26,9 @@ import java.util.List; public class PluginInstanceServiceImpl implements PluginInstanceService { /** - * 主程序id + * 主程序 ID */ + // TODO @haohao:这个可以后续确认下,有没更合适的标识。例如说 mac 地址之类的 public static final String MAIN_ID = IdUtil.fastSimpleUUID(); @Resource @@ -40,36 +41,37 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @Value("${server.port:48080}") private int port; + // TODO @haohao:建议把 PluginInfoServiceImpl 里面,和 instance 相关的逻辑拿过来,可能会更好。info 处理信息,instance 处理实例 + // TODO @haohao:这个改成 reportPluginInstance 会不会更合适哈。 @Override public void updatePluginInstances() { - // 1. 查询 pf4j 插件列表 + // 1.1 查询 pf4j 插件列表 List plugins = pluginManager.getPlugins(); - - // 2. 查询插件信息列表 + // 1.2 查询插件信息列表 List pluginInfos = pluginInfoService.getPluginInfoList(); - - // 动态获取主程序的 IP 和端口 + // 1.3 动态获取主程序的 IP 和端口 String mainIp = getLocalIpAddress(); - // 3. 遍历插件列表,并保存为插件实例 + // 2. 遍历插件列表,并保存为插件实例 for (PluginWrapper plugin : plugins) { + // 2.1 查找插件信息 String pluginKey = plugin.getPluginId(); + // TODO @haohao:CollUtil.findOne() 简化 PluginInfoDO pluginInfo = pluginInfos.stream() .filter(pluginInfoDO -> pluginInfoDO.getPluginKey().equals(pluginKey)) .findFirst() .orElse(null); - - // 4. 如果插件信息不存在,则跳过 if (pluginInfo == null) { + // TODO @haohao:建议打个 error log continue; } - // 5. 查询插件实例 + // 2.2 查询插件实例 PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(MAIN_ID, pluginInfo.getId()); - - // 6. 如果插件实例不存在,则创建 + // 2.3.1 如果插件实例不存在,则创建 if (pluginInstance == null) { + // TODO @haohao:可以链式调用;建议新建一个! pluginInstance = new PluginInstanceDO(); pluginInstance.setPluginId(pluginInfo.getId()); pluginInstance.setMainId(MAIN_ID); @@ -78,13 +80,14 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { pluginInstance.setHeartbeatAt(System.currentTimeMillis()); pluginInstanceMapper.insert(pluginInstance); } else { - // 7. 如果插件实例存在,则更新 + // 2.3.2 如果插件实例存在,则更新 pluginInstance.setHeartbeatAt(System.currentTimeMillis()); pluginInstanceMapper.updateById(pluginInstance); } } } + // TODO @haohao:这个目的是,获取到第一个有效 ip 是哇? private String getLocalIpAddress() { try { List ipList = NetUtil.localIpv4s().stream() diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java index a5175a7862..0a9ba9ee47 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java @@ -28,4 +28,5 @@ public class RpcController { public CompletableFuture concat(@RequestParam String str1, @RequestParam String str2) throws Exception { return rpcClient.call("concat", new Object[]{str1, str2}, 10); } + } diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java index 73c1d936ce..b73f88c537 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java @@ -1,17 +1,14 @@ package cn.iocoder.yudao.module.iot.mqttrpc.client; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcRequest; import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcResponse; import cn.iocoder.yudao.module.iot.mqttrpc.common.SerializationUtils; import cn.iocoder.yudao.module.iot.mqttrpc.config.MqttConfig; import lombok.extern.slf4j.Slf4j; -import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.springframework.stereotype.Service; @@ -20,6 +17,7 @@ import javax.annotation.PreDestroy; import java.util.UUID; import java.util.concurrent.*; +// TODO @芋艿:需要考虑,怎么公用! @Service @Slf4j public class RpcClient { From cde6ebf92189969ace8739724bc6b9ea925d9e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Tue, 7 Jan 2025 17:44:55 +0800 Subject: [PATCH 076/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=9B=B4=E6=96=B0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=20API=EF=BC=8C=E9=87=8D=E6=9E=84=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E8=AE=BE=E5=A4=87=E6=95=B0=E6=8D=AE=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E4=BB=A5=E4=BD=BF=E7=94=A8=20DTO=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=A0=A1=E9=AA=8C=E4=BE=9D=E8=B5=96=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8F=92=E4=BB=B6=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=8F=92=E4=BB=B6=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E4=B8=8A=E6=8A=A5=E5=92=8C=E7=8A=B6=E6=80=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=8E=A5=E5=8F=A3=EF=BC=8C=E5=90=8C=E6=97=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=8F=92=E4=BB=B6=E4=BF=A1=E6=81=AF=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E6=96=87=E4=BB=B6=E5=92=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .vscode/settings.json | 5 + plugins/disabled.txt | 0 plugins/enabled.txt | 1 - ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 8881 -> 16779 bytes yudao-module-iot/yudao-module-iot-api/pom.xml | 7 + .../module/iot/api/device/DeviceDataApi.java | 10 +- .../device/dto/DeviceDataCreateReqDTO.java | 31 +++ .../enums/plugin/IotPluginDeployTypeEnum.java | 4 +- .../iot/api/device/DeviceDataApiImpl.java | 5 +- .../admin/plugin/vo/PluginInfoSaveReqVO.java | 5 +- .../iot/emq/service/EmqxServiceImpl.java | 11 +- .../iot/job/plugin/PluginInstancesJob.java | 2 +- .../device/IotDevicePropertyDataService.java | 8 +- .../IotDevicePropertyDataServiceImpl.java | 17 +- .../iot/service/plugin/PluginInfoService.java | 7 +- .../service/plugin/PluginInfoServiceImpl.java | 204 ++++------------ .../service/plugin/PluginInstanceService.java | 45 +++- .../plugin/PluginInstanceServiceImpl.java | 227 ++++++++++++++---- .../module/iot/controller/RpcController.java | 7 +- .../yudao/module/iot/plugin/HttpHandler.java | 10 +- 21 files changed, 362 insertions(+), 245 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 plugins/disabled.txt delete mode 100644 plugins/enabled.txt create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java diff --git a/.gitignore b/.gitignore index 09ec36308f..49330ee16f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ rebel.xml application-my.yaml /yudao-ui-app/unpackage/ +**/.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..b7a0b8667c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "java.compile.nullAnalysis.mode": "automatic", + "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable", + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/plugins/disabled.txt b/plugins/disabled.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/enabled.txt b/plugins/enabled.txt deleted file mode 100644 index 8cf9b4c87f..0000000000 --- a/plugins/enabled.txt +++ /dev/null @@ -1 +0,0 @@ -http-plugin diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar index 25120abe7f4494bd468cca84c8cea531193ff425..7193d630d82b0166448c05c7550933db82624dac 100644 GIT binary patch delta 11746 zcma)CbyQqS(#PH1-QC^Yo!|rs?oP15-3NDf2p%91+})kP1SeR6B@p;R*z9|6cmMeA zIWt|;r|Q>zt8Uk=sj3F)m=JIjHAP4$1hD68(34g_3561B;CbY}{XGKNz=>Z7b#Uqz z!Uvr3g{TDQfO?hzegfxtA@CstF<)?g1ODra7U+mdN@MQi=Y0_&!NuL()!F=Se9z1LosaREkKqsBFK9~}tN%Z{y^V#V`@h-Ye&4vSzO0t(FFlOm zf`M`ULT3P;k|G0Ibm9H57V)3DCuy2ik;s`Mn6QwevtpPcD5whdse+UtMri`gKELry zMp~Vyag(9|Y1^HOOEX8ewrZ`?&|I8Ma=cvZ zsZ=P*h?TG^T!mv>UvtOSAg^vAmYS+Rnp_29=udX0LC`QnZig#eoApk8J2LzR9Gaqx ztVd12uh?Z8HQa7TGA5Gz_#gqiOO&V3Q z8BPDytd@QU4}GJyo?j-a6((hM`h|antbv8tEE6eU%KTywS-1{wsQ^_PX{*gqznxCJ zGLQrPo4QKcXbFZ6v8Ca%Qt4zeQ}&w?2gfM)rT5Le%N$H_`6N6>PT=JQ24UA5UJtv} zF;YxJ&~GlQkry>IE|7jo?*JcIkKW3&R?c!OuG(wo)uGui&Gr&oL`BBbr-&!=o5t1f zWZR1Zlsd=H&S=&aL(()Hyma=9AW|XHVsyb>e{8emI8?@$gjedGHq(YQ%N`3xT`;9L z$rLo+i$Lof4VPA6s4*A8l&o+L?I_^1T%|ar;>M}6#!dfFPe_Fzkm54KZYT&mZecb` z2;<4@z|mh^n)?!c65~d&Ym8Lr?ntN-lIbo305=VdM!kaTY57mO*0dt@E8Te%PnJuh1KuywPnC=N51y;S_Og`wSSlJ3~c7NIra-m+OL~ zfD${B5uNBnZ>$4tokh>qrCNt~b}`OIV;cz&B5Fi$7wANf&}R`9toTcGDW>YVlvy$p z5q=&@Dv5Q&Aqz+@LSecs%*j$8WR)nqtBPw@8}L}QWg7eT(NIfhR!LN(_w7+awGoYb zP_+pho3w5{zQ8AofZd5*b9IB(SBxP@Hvn()>$*?hZPBy8hy7$}u&jK&GrSzR(w7hx zz`J#~`U-t{$7jqi-W2b63%*H>`y)Cpymx+N_GLgg;+BfM)d+7XxW#U4;m1Sb#M-w0 z2-HLjC4)41RG~HzI*_DLn-Fug{~cU^W|DDfy#enJ>T0!f7A~y$c)hSHy$ZZ=#|gk5 z9Vxa8p={>X3Q?ttc)NLW}28OKWTM4`)GaQV#uosg0O~Z}H-VuG*3s*s(f=Xb~vAxH-U_NJ1`Ou!pGI| zZh8f}oPfPhCyB1UVkiwKSN}UQ3!8R%KOe<*HAOtALDPMU(p!iYowA`U3{=iPhre21 z7Qa@1v9ez^mZI&^Rse|GVWL-Y;NgZY;TtYzLj+vr>?A6z}9TzSGH=i zo#=PF`xr_USF9_ z|LkK27iH?IyO?76p<9uiVk6aB!@;%j2f=7TU=xu}zSLW(GrMFEN@Sp@24%jb7&U52 z?PLlzl8y{G0H5=&!%@lu(62NV!$?z?1|HXSh(-g_<4O?9a0K8ksXdqvzNG5u4 zjZRz5Q+P`4>sTJ7)oT7#GMdI&4=22Zowbi?U;6yfI2d%jLAyOWYTGj){1@f)wy zN9foHh|B*D?+CYI-iBsvDDj-foF=3o*0@;l&W3NY{>N!K7mPpuwS$K zjF*ct`wm0@yDwlA98jM;CrkFlt>AUp)Ay00C(a#dwDycJ9X_5?T>Va=J0py|I5f5> zXBHO0PQkkzx)lLw76e&y?$GBJiQLK)o< zOQD`I+7XRZ?#+P$iIWEfnbds?U<ven#oA73{F0()2WM&yX!%J>!hRi4!FRibKgTN&Xb5JpI-?qqyezp=KGsLHt;fo|?3uW~M zUZ0l#^0r=0DaKm#fxIXyg~XD>aJBBVt21?Wv`Dgy87E+N5XS`5K%6K*6+@c5GYA>N z67H_Q^oxg*>97mR)6x;dcbLq8 zvf=)2DJuV4Xje(nT4MRVS8(Fw_5L^F8%aG=9?efut3b3d&;EQUITG0eTHbslAK(X7 z=%!y^Zh-M`o5m4@ygjQZ>2`E5ttJ9>()g{(RqrSlja6+sgS0%0i_th%Tu7YV+4r|h zuTr;NrsFBC3=?Y>R;#DFLipQq)9w$|4_76oS$xjCs0@^7ysqn0=`80vZrDgx>PhpN zvsD|M(OwG#`U@eT?wFDgC%}-XZby0acXtnZO#|4dE*xadRcjdcXg__M`9MnAhe%RG zC=cBf2|WUo`dVTWFS{kb+#Mry;3zvMuNNQos4y)pCeF-1^NG$EiGOX}^=o8zN%I$% zUD}Im^hW;XV7SLkdHPtc3=S{w%nyO2kNxdm?C#2&9uc3)7G(>b3J;Xym7^vlLwra} zHw>}&O^v^{Mwr7ut_eIhFv^AB=R&+f(15K-vbBuzEg`nfcBCU}E+A_mg#0FmLcoyihWGF)DEN zL$dle@U{R&MVxlUPxsO^HhMVz5yhJ7iU@lO!@VtjgZl5L;RXzI1bJ|y#RP8OAUM%0^rlBM z&0!lpScipz(Oj)GoR3!`N}h4*nS?^>*%o!}QlJF{MGg{0^r##YNs70((plWNrCV4% z!d<4GLP0nCEh9tJjk^x+L}#LWx=1~DW$Hl-iZ?;*w>Nhd1gB(+uG2PHu|3p+2kbB&>lVR=S=*MeXdsS5 z#sq(({TStsaKtl?!5D8o;~D=pQ&{Gd&WnHvXnUwvYZbNS1jC%C#EZ*whrw`zpNMEK z@MCvke>o=eYNqL7-hEYOkW;GJ>Dt*pag$4C83CX6B4SlqAkA^!Vp$ho^atJ!;4 z**I!CyV^KfnK?PRi#t2p+nAfW+c-JC9FZ{fag}v3Ea84i8k!VY`Y>ojDjf2dDcB4O zajF3d=!JrM6K+o!uL@zzI4gyFy&5s5AK-pip!g7}M2elkuFt;H{I}Ov4|k+JUiO0- zz4oE)IQz3Ey6xxy02FKJ)OS4}O3vY#6&?L{$ne#hEwVlc$z+>4CCl)+uYdqLB-Y6p z+~nZ+d_GEh89qx;@XpJp``)pVQ z(k9xBg~01K^PcCmqmJZN4DDHwS4P{^zFjtBnX@X79-VeqfR?c?Y#VY~1^3~jP}4fa zd3L7Tl(h1XN~K+Jx(X>0>X?M5Hr%kyJ*0C#Ir`nuZ3S!?i{P?>G5163N+i`}P4~T7 zAE8~sYx9i-wa0?0w5+x5J|7Dm5i8}^Z{=RZPjQl{e{}qM=pO_n7ltm*xWEpFZ%XbX3J#XfQAW6fiKs-xmFkWPtLo9KtV`_bV^(V#^ktO*c$!EK%Og zH@rt>@XX9cjISY*o9Sc7D(JNfzpBeCmO^7`tx&x`oqNOkWs9uyyA_|vcPdu@8T>(f z5+eT~{QRG8VnSA;$?i?23;0%RzUSZXes&x@oaL1O9I6FjL_vl~B z_Uzi+KE(3*mWQTNUAAm*x4AKsK4W+hKceUHh53vPuh#u(yUJO~&OH8cAQFKeq_f82 z#Z){x+O8M|P@i7qFuY}4uLQciNBErAw8Yj_|4$X$$kjZ&}~&c>J4VLX>Rv24*}8;Vyd<3el#!r_W}f`9&(r?WR@xD z>n|)=_l!j(r0P4$W-2a49`LW#3gzxh2VNr{Q$58Z07_@NF;WyB%^4000pt~F4^EZN z!4LdbuTSYS%$%y7qs=glnacK|I*a>0T#zhv+O%aO(v_B9f#|jp__3#YghGUs&0~r^ zxg|H)plS5#*qC-W?mA{(pFJH!9Wg_W0`?6Do9|pd=Wq!Emtr$M9raPV1w_RtfB0y|SJAkQ zBGOyGzPR~bqQ#$a0ng6Oiy_9Rqrrz`qz<9DwdGB7Fb!L__#95QRuYAMHwsLq_J`Aj zJ7}P*>n5$R3^nIW@RCu2&Bjsmsr{j2Oyp7}0l*K$T%~sbE5R#J*)hH1DTHX?EP|uk z(%?qkvA4o}1_%5gILk;Z`n5z~ycUC-$iNqc-f#zxxqH}=1)7)Rg5Hm&U%8|AJs#1S zf{p&I`nH(*lg4W{+dfYcj=M6QZ)CHyU2k-+VZ2av?0u^@eXbog!3}*^KR}1W&|DZ+K}Ar3z1QyCp*K<#VqH{uj-ykBE5w zDuq8q^nGW|+GxuG;5U>ccz)Zn$(rG)A>ROctii<{zn-EIQ7MylxEegfaT(JOl=T`+ zOr&8p=ouu4gfV?L(k8TKrkLt0g>YZAn3Q7r5YSinbpy#!TS<4QN`!>Jak8ivAd#Ze zs&J0T@=Xo9y@7Sx{lUjF;I<3v7JK7j7<+(6IKjR(>_VSsZpJqJhIH6WD1s-$yea_z z+5P&C$bQqe5G-$634khZ{3n$)ENqPMV zdPP&^|izU=rYMlC(FJbz_5+Yh)#eGSEHD&_P&jM``#&14L!(X2Tjmw`&U^`y0p z!Wfji4f8zEy;I1rNP97yULHC5VgU$nZ_Hty6)3Wxx1o|7v!ygmZa$5R+ZytZwSykA z*yE2KDd-G@$H%xUuE%9NBAtIa##3I%*UkC2X9xKi`inS>#!MZdvS9F?CZJ{@v=w~~CaKg?%Q#F{A~D@tq)uxu>5`9$XhzmuMLKF`{+20V1~6HJLh> zifXn*GaX5egKV@996ls5*qMmc3$mtdUHJTr+;q4hb-+fY28-4+$N1Epmso--ejaPs z)91X4x14u)%J+j8Y%t{1k7QIwFDp;WEnmQs?|DA&{h_86DMY;{*L_liA2c)rf;yF2 zfD+-7g!&{N9Pt-Hn*Mk(^HcqZT0ySmo%%*~oG&$BE3~SSl5Qj)wHo&6BJMb*J>PwV6snVSMKdC=qO- zJSPer4fR_c1q(g#Ict+p-ciTZUlkL44`AvSK)JYvn6AuX5l~c}x=>19RPzU9S)-_DProcPRf{Hx73{EC39z@+4%ksSI|0D-Z*PhpNX6p;yfqP#3yYVnH)l~>&_fZ zcVM-Mq#|gAx-!H}wVeBhjrgbv-q>%xO2xc50MDFzsE?+s`3^DHsk!FjLy4N{ zBD`Tb8t_vJ`qC;Py7&-V7jHhCN;6@fXf{{er;&KlBugKNoN;7?T`-~ra6!&vR3o5d zwT4WNU5=n$#jPG=o-ePb8O`j!dez#s30&HTX=o;RUN{<6%e(ZYw0r@jlXf?$Fb#vg|W^+?dYkRicc%BQei7rq&m#FQq8MB3E|z) z4JMbu9?-&Lqt7a2g> zTA4xrz{)e?8@vP5GGm+g;5X-DF=Z5&jNs_D{>ikYic0Cu3n$A}UiOI1FED{<6=;Y5`>8Bi19cFGxR%5U7-d?t3Kzej16B3CZn5+&z>8-0Tg`#VZ0S> zh)yMN@V+ssvGYah0JBzUU`ttWzt9wjuqxr}E^oCr;+8_=Jh1%2er>E+q%-#z!OVL! z6untP1?49D6`Ls%{=N$(9?$4l#Yyv$oPfhtCCDg;6MC3Ch6{jt_R2XS`WECbC5cJq z;r0HkFr%4^(rl>lZzgPysbhA(P9IdurG&qYJFZHsNm#W#4NI8hpJJhq=yNI=F zT}odWw`Uay@E+m*hV_-7omBIZPefJ-PnA#8ke~Q{=C^kc-|vE>SwV`;DXDBwb?rTy z4mTr(&0HH=3`05>+R(2xFFpJuO>CAHM5XXIlp~1x>g=4xdm(*0pwg_~&Cg3l)qV_f zV!VrwJ_q?ioaCu%tE#Ay!b8>7jg{S{x{c(Y2!UMi0N}=fq!rJZ-1~5x1kMvCQP#Kj zm@*kQnyr0uC^7WtUY3W)BjBtlxW%5)M79^v$IJ zwIJS&(wQwM!<%6h66y=H>` zj#^9-<#1C07TR?~Na;!4e15Es;Bl(BiDLReqaT5V?6weId(OKKIZ<8vR|@9w!=sg@ zQvEDHFe|3n2-*vK#>J8D^ZDQM@SwSt)E|AzY3O%p76RXWR)NW zBt11O3>~@g!=`)JtWEm3zj(y_*ou(<0jjg&Nd^i+Z3e zBsFG!RFJLs$^(3VED`<=-WS!w2vh}6i0m*0B52dqRP6ITz#?c%;x1>H({hABL zp(yte2&`UmL zxoFL2tf?>L%2*R+S2nbC)saOx_*K3uW)}pu<(&H_w^2mhWWl0WDT1o*<>JwpJTbL8 zSc(y>;E%lNam@IQLRN-8y{pGy)|IyKq!y?r=+X^gTS92<4BS76kr)!20YDkoMS@)@ z9g#`inL>^ie*#f;OoLX%CiM)Fsa#HfMrt9c1aO9%f6q{>UI;5nGpUk$qhxJYsG)3t}iAJ{@&pT^QU;W6F^?;)KNl51tH=U&~ zd|w>Y5{-RXH}OjFcmww4O1z!As>iG1i}?!IUC}T{=zHuOE$6XwFH@!|OzJmX#}aI+ zDMdZpC+r;2w3|LK=PIC|b;tHb1=~y_Pz%7u+v|lTXatC`V()^FbO1GXm{A`KB1&O! z>ZqpTJL@jiZOT2;EaxFwh>uhv!tt6|?D@5BvV@L9h4ew3 ztglmM751(^L}od9MgkCg{9R}gJvBdG^kXauN98v;n%|{@3GTc%zw_TJ$=Kl_J}l%C zP{6y@urD zt;nx@Tk5Y24dbsYoU*i*IJ3Np46EDkWE=BK!-j|J?_3+RgN3{48&h{v0E?}glVev( zg94;DTKK)tpsR-?Y0oRd7%VdZd6zR~kZogA=BB13T!r)uh|B~55jrZ446l}AXn1t* zih08j4O%>gav4cbv`O?E`-Hn<>|5kMIYOtn)3(?KN1WWcU>v?hyO!GPvDa0nhf3Jd zidc9Mmnv8=BL;Xf%Fac5fEdyeXxxh9#$HAiqv^a)JTR0PYLgaeZ?6q&nC*p?D}ri5 z0lIfAJK!@;B|o>>wkO57?^>xV1KbIN(WzQeD{#V6Yi&~}%e;;X@(o3)d1|iMbHTo5-KG zWSvi#)xIQ?DdwoBy5tr&B00z#waDp2Nf#TH&iRP_ks=9vEHsDe=wi!3jw4dX=fh1& z|DLD|+RtoxMA_p2i5@)rZ{mvf^YZFve z5N{|$>C&N>efoDNV<&bPKdgLi4y1D$+^rucmydwjI;P+Oyve8T%jgr?aBd4Z09c?9 zTqFKb$V+1qFcX*lrCDhRm;Z%;#CwJQtgCJf_(mcqFtD&+ijoAP;fVq(@t6Q6f}5N* z`}MLaaLsdhe9j@b+ri$1D|_q}1M>Fe^O=?zIUrZoz zA;&b6*gtl{bw3_UU%A|I@kcAt*V$Bs5DrdhcN9UtrPnATEKXh8;~#=vRg0ICHz9;? zA4q@Q*=J=PZYf?{IHcvMWf=-kFOD=Jh6#+UhTI631^0`);om-HZe3?y%j4;wZCYi2 z++Q4LB^*Kjtbmy}#|A#%hK4`+ZekHQWF9N5SF42~->#BX3@XLa{{A+tgoqtnya;15 zY=-@eCzc>7i&?Hp+}XfFN@}}h%w`}oeymfUgT!2R7_Wt2PGtr@OF9|=)Tp&xCWJ+5 zwWdL>glsq{37Og{DHsJVJIp0rJL7(~eD`XsLV=pm4uy*JVvu$OAX9gY8PAB|cpL zO#Gy-+-W|%1!*%mpF_6?DEkO6$YBawC{qiv+PoaL=9k~<6K>{zq+QAJGF{MT?mx`efKu-R_fPC4!^G4nZsYstSxGFho%v$k)a$=~z z_`F|(WvY&Lx(=HH(&Sc1O`Bed(uOi+aHWPc(6VFZyi!7=QQh4L5TDt&r=`lrH%(`0B3SeC@XL)LU&1(C z4!9K+N7L-BndYk305OZOx|P&mB9i@R!0_01)`&^3D}i|$vilgex)@$^-6+P*u$~g!hO~w_HjVkI9RTqBZh|(F{?3cMeVC1gy|bk`qBB4hnS($4b*4 zUb*OMC32rm2e@OP*Pf7b2qwzI4tqtr{!F3TO)T@I1)uB98Rt7Y+j=6yb2{CoDnWHl z_?D4!9rIQG1k$xN@Pl}d=k8RO%)1M);~qk1I=iVM-6qi#sWQe^e40+O9Oi;Kkua-w z`JOrcoKezX;DD-TLaM@Iz3(J3R+AG;gxslir+KSUj>}h7P9=}JR+I0-Z*PbVJ^PK$ z3kh#)Vic{wh`wEJ;J@+ZopQGm7AB4FG@r^L+4FAFSl)ELYi7zGd8M2#9!#2$8pwrv zD`a(_S-w=GR8TDe&)xlmZ1SFClKA~*SLW(TUJ}tyL^XncwMV`bl4f~F)dPOH(WqY^ zh2JhBjsgXMJg$fk!iEMmQ4N;tF+cOpF2j%p7Ibm5SlHQaY zbspSlBTAd|RWL5c3ZvoLM>}8Nkb5gAE2Hu>DsGXVY)$YSG39S)l8V?;F9zFl+_e&l z0H6~Y0l8T_diCL2mY_uydf1&_e5;zNSoU{TPmpS;ir^4fkbm`X0!xWGUph25h(%rq z5)$SYLY0L3g@_~(d?AKOBwpMgGO5f9p+l-j@jtpaf8FxSg$8yit^@&eLM7q-TZvii zZ@c)fw$4AIV9&Bizh$}pWc&Uu_+NdV{}7!2EhzX$@Fj}))yMhI+yA%7FXB1)c)6Yz zCW3t4^k4FS`au84zu5R6ouGfje~l$U7SHnZzZygFUlfHvMgT#N@@tM2Sc?P)G$KQK znRoa#Pk{bwo*Vd)41wZ*)aU;JtAYQ#E$qMP@~?6|@RAgp>{%=SC}I9XJecTNT>bx5 z(fmtSFPr*D8SNjab|lZJivOW&<^QOv{U_c(a=QP(6Q+8`1NC9;Q27T~`ne+fzv805kc-&=D@yt!{mk@i3*gi*12=F0kI9H%430=n0I|gZ zM8_is%8?`ew$~rP=TYe2KhVIQo(MUAF^U1RND;{XE9&?+n z+)#Sf`j%(2hH~3>vdYJG!!*{%6@9E_9Iz7zHXN`fJvj^Z_uOsUo%0<1o%M6I@hTfa zY>sugA&V=7)2v{9KQm8b?4ryGij==}Ll>5Ld}h?ewbW@D^>@?{6jp}|t2(f7Z4u6H zQRoS}S`P4V?F@&~LNFznJ?;zXM(CmQBO7BX&Z8@03QzGGVklDV$Bz2*^>gyiGBv;t zyPT917<9WlI;_=11S2gpndtF2i%+tMF3)Pu%RFC4XJ8&ElD{9)7Wc@9zEpmd113B_ zS8YI##VNIbwhy*bozlHP>g74~C%s$LzGIoXS9 zb>}4zI=4sDs@roBe<<64_6gzoYD`Zyuwv;5)P}yM6=eT#6MvTy-Z5&jo+5NOjBT zn1@}lO7btUdLtb^1deQS>oG^NinkP0khnjX62i9_HDE30W%-&271*r}2#odJe#JR$ z)G-NpE7utElUWf2>2Qr06{0YHI{#_}gha@S%faV+{6ZfXvX0jrN~u2E?i_hbqRpKT z!v3ROwJ=FhlXt_ILJ@JOzEsa3Y>5bABN>^|VCKj@<9#}jfB8JB_)HYmsrA&T4hx7a7m_PCMF8nlfX6#N+|UB@e`cfw3f2UlpmRxie};PZLFWiAr|gj-Bbve-sR z%F`q(Pg7472`n}+ZF(*H0FAnV(SsIZTv7Olf~2vgUbt+Oqug%~i|S%x72CWi?s448$aH8!jqV0L2?QQ36mpRp0M9x@vn;R-5 zP@x^~mk>FuY(fK4keJ42p&e)BLEF1Im%mzj!pc$B0{O=M{bhS%NkGPdhUQs4J2;re z&TUzCTtH5xbzfjZV6i8GHZf@s&$`|$zPUD$n|%u!MW3i8P@$0~w)Mj_)|{p?F5%bO z;%~nv@9`9Z?&7_z(o6}Ip!ZMKL(1h<&U3du6Cl^hs3mK|FC$*g4JlEe>r$p(HOr=y z_)ML5Uzxh6hVto(N@d6BCm~9mZ1V8-vT}=LnOl?ZGb*+}>O;%y zPK}7Odv!4S{pRR$q83D0wdMpP*VQJ<}bm z{pCz{THIn;g=SuK&G!rlLl#B8y`~1goO5w>|9Br4ec`KC#?9qt}qCtx@d++QP#2Q^s7RTbx8EIbbyw8l@;a_o9 zSlV0ir1~hOqc^}IfFua);N!X%<%EC2-TiYy92tUs3M*k}t@enijgS)zHwxUWKE1+! zk*ave6O^NGR)Qj>IrY^VRIN!Ghf*=op-Q1LX#au21%EdSX^-`t-}3J=$MJ~x80J9n zu*tgu;m66TS$%|2Qy#k&Vyn6>K~;yRJKZn#_>NjkQrvk0fLrbAL42JVX@S*;Epwwm zvA+UxxF3D>&)Dt=H6_&z+rl@=aI9Okrk68S-5?+j{Ow*Lw&Q7|r*Cs^0-a6K!yJ4* z;)c2Pea7I&af?=79@iQ^SGgoBj?oIlF%*_>mWfr7>1&-s$c7gMkwBLjKkZ1K7*E8+n zYGtZWO6HpopR(jHi(S_2@dUfi$FP?QF(^r87w?N9-(HF=EAqj`Omc@7&?giyKE8~Z z&bk@|uUte*F4ZIE4HEpTP9F?UoQo6N;o_FFO{G-rGd93XVUE3lMIHGrNI_F)UDAPoxpl@M97wv+9EA; z^A3|)%LHx&%rjP*6iN?+*ii?vSc^HPy^`Wowe{FuJ8lV{?YZaoiXkIAdc!|CJGjY~ zd$0in@+Il#p+RN!E`QZBhZ;g4r<_6SEYcskw}hoT=P&EO@%Qd)Ob?tBNC$@APiThE zfS(ry{m{S-4%`qn9hI4W0pn*PF(DyIG=Ip@{p5+|1OHF@tu`TUns(M-C|hMd!ew4c z_5SAPf@?$mlG=-r9qc@4VLb$j3U zL<;W0{2NOO+LHsZLjJ~qc0XD?XZ*W&yzTkwMM2w`e>Q^k93(>cxVIf80xiUO+a3NP z1i1yAV13X{lgW|v{sMpv0GxpkaIgz>z^B#DU?g-o`!0Sg8J^1?rpc#wBbQRRDPpnP zUg8rlwvuL(M>*xvY1}UZp00F8sr6!`@e{{*2syMA`%Vs+FD~1t#@3c5Ryg>Inu|+8 zF60u=HsUB(id14qN*tY*9`WZjknMoPOt?8%MjjgdPXDy5tQr4nL z6g;+We4@(oL)>1jSDmx$V2ei;Vu_igz>2(+XEoFfhs||W}x;LG(dvziegZzN1qi&QDL)9CZb(SJPj9ntPdqQiH3iDL#8$Lv)FPSg=p^UG^h349R; z3N+?InzWbIR?F7Gwz6-Tx!!#(T3iycI-{S+j!$>H)omn`B22v;u|Zi&#Y}o?%?Eb~ zCg*`I49ksL;#PMj14!n$N=so)UeaAol7zvGsUGe=Quo^QV+cd${c_8m3@DA;a>Umf zJ_3v!DWcaXlak>1TMie{mBvC^{i|>O)4Q^q;AbLKy(Sgx7Cu~v_pm4ZefQh@hXu%D zo>scD3yRW^3?8b^*sW6s`^1?FJU$2{-K=Qi23H&Gh*D1~3v9V;Sb;xMEg9(tx%&Qc z!~BpH(e-;%0MTN9kyZj|%j z45siq77@_lmp!>UW%KJ{dyHdNW5YVgr#lmQSyLFm5k=&Q%{mePX?=0bj=gR?0WlCa zjFES5E)t``b^hFeHfSQa6o}Q1MVNR$e*fDh^Fm03JfJRp4HaFxj-4e9 ziU-ye2mhhWPBPXC&7P~n%J6b~S;8@$$R-D}ckm@;q9;s)E${sCn>Z|E{4uxO8Q_;# zqN<^9GQSa{c0JA;BGRS@zM~=m5qR2o0q^rFAhcE^m%5}GRWr5*dtq)(A(_Xl4IM9g z+>S?1$S|;*i{_7TKgOi&*7{8ms&(jidRESZC6u<2ra!CGppVoI56hQYg7MPk6($4M zZF#o7k7u;@1xflA_kV+fpBQO6W~-B@b{WONPS1-o_2{v)VwT|% z^Aj(_uWwYh4N$|vV#}~e+Q>RyyDKbhbqq{Ga;*RQVZ^!^_)kNOV1!8Do?aFZ-dm6a z5xE6J5Q$p=6qmaN+Tu#L04c7{{J)ME01&>h+kZu2s0Jn?6zd_+&D^(B-wJXMO((N4 z0f0&z06^q#_Vrj8YDsu4;6#{_;3D=V$(e3^x_|{xWDO4h*dqo2!2cpYCH{*{h%k}@ zn%y4C|Azs%R{Rxr0asV?Q~-dcqOL5j>SHB-XA2)27ykbi_W#98c6<=+>m}!Azp)}5il7-rhi`o0D$~I#G7SmQC|}k5x=jAH%t7V?eY(`Ftmp5 enkaX@cYu`I>NwZ;4&b_)y8ztlAD2RZll=$Xda`c- diff --git a/yudao-module-iot/yudao-module-iot-api/pom.xml b/yudao-module-iot/yudao-module-iot-api/pom.xml index d2f83b785e..cade52eeaf 100644 --- a/yudao-module-iot/yudao-module-iot-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-api/pom.xml @@ -33,6 +33,13 @@ + + + + org.springframework.boot + spring-boot-starter-validation + true + diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java index 076064db82..6eed3592b5 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.iot.api.device; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import jakarta.validation.Valid; + /** * 设备数据 API * @@ -7,14 +10,11 @@ package cn.iocoder.yudao.module.iot.api.device; */ public interface DeviceDataApi { - // TODO @haohao:最好搞成 dto 哈! /** * 保存设备数据 * - * @param productKey 产品 key - * @param deviceName 设备名称 - * @param message 消息 + * @param createDTO 设备数据 */ - void saveDeviceData(String productKey, String deviceName, String message); + void saveDeviceData(@Valid DeviceDataCreateReqDTO createDTO); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java new file mode 100644 index 0000000000..94bc84b804 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.iot.api.device.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import jakarta.validation.constraints.NotNull; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class DeviceDataCreateReqDTO { + + /** + * 产品标识 + */ + @NotNull(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotNull(message = "设备名称不能为空") + private String deviceName; + /** + * 消息 + */ + @NotNull(message = "消息不能为空") + private String message; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java index 9261e4ae1a..263873be7d 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -13,8 +13,8 @@ import java.util.Arrays; @Getter public enum IotPluginDeployTypeEnum implements IntArrayValuable { - UPLOAD(0, "上传 jar"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈 - ALONE(1, "独立运行"); + DEPLOY_VIA_JAR(0, "通过 jar 部署"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈 + DEPLOY_STANDALONE(1, "独立部署"); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java index b4a2a62dba..eea7b2a963 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.api.device; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -17,8 +18,8 @@ public class DeviceDataApiImpl implements DeviceDataApi { private IotDevicePropertyDataService deviceDataService; @Override - public void saveDeviceData(String productKey, String deviceName, String message) { - deviceDataService.saveDeviceData(productKey, deviceName, message); + public void saveDeviceData(DeviceDataCreateReqDTO createDTO) { + deviceDataService.saveDeviceData(createDTO); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java index ad3b31fc1c..25c0f6bcb7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; +import lombok.Data; @Schema(description = "管理后台 - IoT 插件信息新增/修改 Request VO") @Data @@ -39,6 +41,7 @@ public class PluginInfoSaveReqVO { private String protocol; @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED) + @InEnum(IotPluginStatusEnum.class) private Integer status; @Schema(description = "插件配置项描述信息") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 2c1553a722..3c21a55ca8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.iot.emq.service; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; // TODO @芋艿:在瞅瞅 @@ -16,7 +16,7 @@ import org.springframework.stereotype.Service; * @author ahh */ @Slf4j -//@Service +// @Service public class EmqxServiceImpl implements EmqxService { @Resource @@ -34,7 +34,12 @@ public class EmqxServiceImpl implements EmqxService { String productKey = topic.split("/")[2]; String deviceName = topic.split("/")[3]; String message = new String(mqttMessage.getPayload()); - iotDeviceDataService.saveDeviceData(productKey, deviceName, message); + DeviceDataCreateReqDTO createDTO = DeviceDataCreateReqDTO.builder() + .productKey(productKey) + .deviceName(deviceName) + .message(message) + .build(); + iotDeviceDataService.saveDeviceData(createDTO); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java index 47e7bf5605..d32148b47c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java @@ -22,7 +22,7 @@ public class PluginInstancesJob { @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) public void updatePluginInstances() { TenantUtils.executeIgnore(() -> { - pluginInstanceService.updatePluginInstances(); + pluginInstanceService.reportPluginInstances(); }); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 08375cb092..a882b5d6cb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import jakarta.validation.Valid; @@ -25,12 +26,9 @@ public interface IotDevicePropertyDataService { /** * 保存设备数据 * - * @param productKey 产品 key - * @param deviceName 设备名称 - * @param message 消息 - *

参见 JSON 格式 + * @param createDTO 设备数据 */ - void saveDeviceData(String productKey, String deviceName, String message); + void saveDeviceData(DeviceDataCreateReqDTO createDTO); /** * 获得设备属性最新数据 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index eb7fcd430c..b39df35901 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -6,6 +6,7 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -14,8 +15,8 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; @@ -56,7 +57,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe .put(IotDataSpecsDataTypeEnum.FLOAT.getDataType(), TDengineTableField.TYPE_FLOAT) .put(IotDataSpecsDataTypeEnum.DOUBLE.getDataType(), TDengineTableField.TYPE_DOUBLE) .put(IotDataSpecsDataTypeEnum.ENUM.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? - .put( IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? + .put(IotDataSpecsDataTypeEnum.BOOL.getDataType(), TDengineTableField.TYPE_TINYINT) // TODO 芋艿:为什么要映射为 TINYINT 的说明? .put(IotDataSpecsDataTypeEnum.TEXT.getDataType(), TDengineTableField.TYPE_NCHAR) .put(IotDataSpecsDataTypeEnum.DATE.getDataType(), TDengineTableField.TYPE_TIMESTAMP) .put(IotDataSpecsDataTypeEnum.STRUCT.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! @@ -128,20 +129,20 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe } @Override - public void saveDeviceData(String productKey, String deviceName, String message) { + public void saveDeviceData(DeviceDataCreateReqDTO createDTO) { // 1. 根据产品 key 和设备名称,获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(productKey, deviceName); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName()); // 2. 解析消息,保存数据 - JSONObject jsonObject = new JSONObject(message); - log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", productKey, deviceName, jsonObject); + JSONObject jsonObject = new JSONObject(createDTO.getMessage()); + log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject); ThingModelMessage thingModelMessage = ThingModelMessage.builder() .id(jsonObject.getStr("id")) .sys(jsonObject.get("sys")) .method(jsonObject.getStr("method")) .params(jsonObject.get("params")) .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) - .productKey(productKey) - .deviceName(deviceName) + .productKey(createDTO.getProductKey()) + .deviceName(createDTO.getDeviceName()) .deviceKey(device.getDeviceKey()) .build(); thingModelMessageService.saveThingModelMessage(device, thingModelMessage); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java index 6a44747a6e..2e920e32cb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java @@ -78,9 +78,10 @@ public interface PluginInfoService { List getPluginInfoList(); /** - * 获得运行状态的插件信息列表 + * 根据状态获得插件信息列表 * - * @return 运行状态的插件信息列表 + * @param status 状态 + * @return 插件信息列表 */ - List getRunningPluginInfoList(); + List getPluginInfoListByStatus(Integer status); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java index c9030b9244..8e1fae88ab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -9,25 +9,16 @@ import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.pf4j.PluginDescriptor; -import org.pf4j.PluginState; -import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPluginManager; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.IOException; -import java.nio.file.*; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_DELETE_FAILED_RUNNING; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_NOT_EXISTS; /** * IoT 插件信息 Service 实现类 @@ -43,11 +34,10 @@ public class PluginInfoServiceImpl implements PluginInfoService { private PluginInfoMapper pluginInfoMapper; @Resource - private SpringPluginManager pluginManager; + private PluginInstanceService pluginInstanceService; - // TODO @芋艿:要不要换位置 - @Value("${pf4j.pluginsDir}") - private String pluginsDir; + @Resource + private SpringPluginManager pluginManager; @Override public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { @@ -75,32 +65,13 @@ public class PluginInfoServiceImpl implements PluginInfoService { } // 2. 卸载插件 - // TODO @haohao:可以复用 stopAndUnloadPlugin - PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginKey()); - if (plugin != null) { - // 停止插件 - if (plugin.getPluginState().equals(PluginState.STARTED)) { - pluginManager.stopPlugin(plugin.getPluginId()); - } - // 卸载插件 - pluginManager.unloadPlugin(plugin.getPluginId()); - } + pluginInstanceService.stopAndUnloadPlugin(pluginInfoDO.getPluginKey()); - // 3.1 删除 + // 3. 删除插件文件 + pluginInstanceService.deletePluginFile(pluginInfoDO); + + // 4. 删除插件信息 pluginInfoMapper.deleteById(id); - // 3.2 删除插件文件 - // TODO @haohao:这个直接主线程 sleep 就好了,不用单独开线程池哈。原因是,低频操作;另外,只有存在的时候,才 sleep + 删除; - Executors.newSingleThreadExecutor().submit(() -> { - try { - TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 - File file = new File(pluginsDir, pluginInfoDO.getFileName()); - if (file.exists() && !file.delete()) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); - } - } catch (InterruptedException e) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); - } - }); } private PluginInfoDO validatePluginInfoExists(Long id) { @@ -127,144 +98,52 @@ public class PluginInfoServiceImpl implements PluginInfoService { PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); // 2. 停止并卸载旧的插件 - stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); + pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); - // 3.1 上传新的插件文件 - String pluginKeyNew = uploadAndLoadNewPlugin(file); - // 3.2 更新插件启用状态文件 - updatePluginStatusFile(pluginKeyNew, false); + // 3 上传新的插件文件,更新插件启用状态文件 + String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); + pluginInstanceService.updatePluginStatusFile(pluginKeyNew, false); // 4. 更新插件信息 updatePluginInfo(pluginInfoDo, pluginKeyNew, file); } - // TODO @haohao:注释的格式 - // 停止并卸载旧的插件 - private void stopAndUnloadPlugin(String pluginKey) { - PluginWrapper plugin = pluginManager.getPlugin(pluginKey); - if (plugin != null) { - if (plugin.getPluginState().equals(PluginState.STARTED)) { - pluginManager.stopPlugin(pluginKey); // 停止插件 - } - pluginManager.unloadPlugin(pluginKey); // 卸载插件 - } - } - - // TODO @haohao:注释的格式 - // 上传并加载新的插件文件 - private String uploadAndLoadNewPlugin(MultipartFile file) { - // TODO @haohao:多节点,是不是要上传 s3 之类的存储器;然后定时去加载 - Path pluginsPath = Paths.get(pluginsDir); - try { - // TODO @haohao:可以使用 FileUtil 简化? - if (!Files.exists(pluginsPath)) { - Files.createDirectories(pluginsPath); // 创建插件目录 - } - String filename = file.getOriginalFilename(); - if (filename != null) { - Path jarPath = pluginsPath.resolve(filename); - Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 - return pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 - } - throw exception(PLUGIN_INSTALL_FAILED); // TODO @haohao:这么抛的话,貌似会被 catch (Exception e) { - } catch (Exception e) { - // TODO @haohao:打个 error log,方便排查 - throw exception(PLUGIN_INSTALL_FAILED); - } - } - - // TODO @haohao:注释的格式 - // 更新插件状态文件 - private void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) { - // TODO @haohao:疑问,这里写 enabled.txt 和 disabled.txt 的目的是啥哈? - Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); - Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); - Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; - Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; - - try { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew); - if (pluginWrapper == null) { - throw exception(PLUGIN_INSTALL_FAILED); - } - List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) : new ArrayList<>(); - List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) : new ArrayList<>(); - - if (!targetLines.contains(pluginKeyNew)) { - targetLines.add(pluginKeyNew); - Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING); - } - - if (oppositeLines.contains(pluginKeyNew)) { - oppositeLines.remove(pluginKeyNew); - Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING); - } - } catch (IOException e) { - throw exception(PLUGIN_INSTALL_FAILED); - } - } - - // TODO @haohao:注释的格式 - // 更新插件信息 + /** + * 更新插件信息 + * + * @param pluginInfoDo 插件信息 + * @param pluginKeyNew 插件标识符 + * @param file 文件 + */ private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) { - // TODO @haohao:更新实体的时候,最好 new 一个新的! - // TODO @haohao:可以链式调用,简化下代码; - pluginInfoDo.setPluginKey(pluginKeyNew); - pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); - pluginInfoDo.setFileName(file.getOriginalFilename()); - pluginInfoDo.setScript(""); - // 解析 pf4j 插件 - PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginKeyNew).getDescriptor(); - pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription()); - pluginInfoDo.setVersion(pluginDescriptor.getVersion()); - pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); + // 创建新的插件信息对象并链式设置属性 + PluginInfoDO updatedPluginInfo = new PluginInfoDO() + .setId(pluginInfoDo.getId()) + .setPluginKey(pluginKeyNew) + .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) + .setFileName(file.getOriginalFilename()) + .setScript("") + .setConfigSchema(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) + .setVersion(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) + .setDescription(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); // 执行更新 - pluginInfoMapper.updateById(pluginInfoDo); + pluginInfoMapper.updateById(updatedPluginInfo); } - // TODO @haohao:status、state 字段命名,要统一下~ @Override public void updatePluginStatus(Long id, Integer status) { // 1. 校验插件信息是否存在 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - // 2. 校验插件状态是否有效 - // TODO @haohao:直接参数校验掉。通过 @InEnum - if (!IotPluginStatusEnum.contains(status)) { - throw exception(PLUGIN_STATUS_INVALID); - } + // 2. 更新插件状态 + pluginInstanceService.updatePluginStatus(pluginInfoDo, status); - // 3. 获取插件标识和插件实例 - String pluginKey = pluginInfoDo.getPluginKey(); - PluginWrapper plugin = pluginManager.getPlugin(pluginKey); - - // 4. 根据状态更新插件 - if (plugin != null) { - // 4.1 启动:如果目标状态是运行且插件未启动,则启动插件 - if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) - && plugin.getPluginState() != PluginState.STARTED) { - pluginManager.startPlugin(pluginKey); - updatePluginStatusFile(pluginKey, true); - // 4.2 停止:如果目标状态是停止且插件已启动,则停止插件 - } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) - && plugin.getPluginState() == PluginState.STARTED) { - pluginManager.stopPlugin(pluginKey); - updatePluginStatusFile(pluginKey, false); - } - } else { - // 5. 插件不存在且状态为停止,抛出异常 - if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { - throw exception(PLUGIN_STATUS_INVALID); - } - } - - // 6. 更新数据库中的插件状态 - // TODO @haohao:新建新建 pluginInfoDo 哈! - pluginInfoDo.setStatus(status); - pluginInfoMapper.updateById(pluginInfoDo); + // 3. 更新数据库中的插件状态 + PluginInfoDO updatedPluginInfo = new PluginInfoDO(); + updatedPluginInfo.setId(id); + updatedPluginInfo.setStatus(status); + pluginInfoMapper.updateById(updatedPluginInfo); } @Override @@ -272,10 +151,9 @@ public class PluginInfoServiceImpl implements PluginInfoService { return pluginInfoMapper.selectList(); } - // TODO @haohao:可以改成 getPluginInfoListByStatus 更通用哈。 @Override - public List getRunningPluginInfoList() { - return pluginInfoMapper.selectListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); + public List getPluginInfoListByStatus(Integer status) { + return pluginInfoMapper.selectListByStatus(status); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java index 5655f1d3ad..cd1d5a6547 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.iot.service.plugin; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import org.springframework.web.multipart.MultipartFile; + /** * IoT 插件实例 Service 接口 * @@ -8,8 +11,46 @@ package cn.iocoder.yudao.module.iot.service.plugin; public interface PluginInstanceService { /** - * 更新IoT 插件实例 + * 上报插件实例 */ - void updatePluginInstances(); + void reportPluginInstances(); + + /** + * 停止并卸载插件 + * + * @param pluginKey 插件标识符 + */ + void stopAndUnloadPlugin(String pluginKey); + + /** + * 删除插件文件 + * + * @param pluginInfoDo 插件信息 + */ + void deletePluginFile(PluginInfoDO pluginInfoDo); + + /** + * 上传并加载新的插件文件 + * + * @param file 插件文件 + * @return 插件标识符 + */ + String uploadAndLoadNewPlugin(MultipartFile file); + + /** + * 更新插件状态文件 + * + * @param pluginKeyNew 插件标识符 + * @param isEnabled 是否启用 + */ + void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled); + + /** + * 更新插件状态 + * + * @param pluginInfoDo 插件信息 + * @param status 新状态 + */ + void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index 6a65fc0265..618d09c733 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -1,19 +1,38 @@ package cn.iocoder.yudao.module.iot.service.plugin; +import cn.hutool.core.io.FileUtil; import cn.hutool.core.net.NetUtil; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper; import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper; +import cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginState; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPluginManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; +import java.io.File; +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; /** * IoT 插件实例 Service 实现类 @@ -25,79 +44,195 @@ import java.util.List; @Slf4j public class PluginInstanceServiceImpl implements PluginInstanceService { - /** - * 主程序 ID - */ // TODO @haohao:这个可以后续确认下,有没更合适的标识。例如说 mac 地址之类的 + // 简化的UUID + mac 地址 会不会好一些,一台机子有可能会部署多个插件 public static final String MAIN_ID = IdUtil.fastSimpleUUID(); @Resource - private PluginInfoService pluginInfoService; + private PluginInfoMapper pluginInfoMapper; @Resource private PluginInstanceMapper pluginInstanceMapper; @Resource private SpringPluginManager pluginManager; + @Value("${pf4j.pluginsDir}") + private String pluginsDir; + @Value("${server.port:48080}") private int port; - // TODO @haohao:建议把 PluginInfoServiceImpl 里面,和 instance 相关的逻辑拿过来,可能会更好。info 处理信息,instance 处理实例 - - // TODO @haohao:这个改成 reportPluginInstance 会不会更合适哈。 @Override - public void updatePluginInstances() { - // 1.1 查询 pf4j 插件列表 - List plugins = pluginManager.getPlugins(); - // 1.2 查询插件信息列表 - List pluginInfos = pluginInfoService.getPluginInfoList(); - // 1.3 动态获取主程序的 IP 和端口 - String mainIp = getLocalIpAddress(); + public void stopAndUnloadPlugin(String pluginKey) { + PluginWrapper plugin = pluginManager.getPlugin(pluginKey); + if (plugin != null) { + if (plugin.getPluginState().equals(PluginState.STARTED)) { + pluginManager.stopPlugin(pluginKey); // 停止插件 + log.info("已停止插件: {}", pluginKey); + } + pluginManager.unloadPlugin(pluginKey); // 卸载插件 + log.info("已卸载插件: {}", pluginKey); + } else { + log.warn("插件不存在或已卸载: {}", pluginKey); + } + } - // 2. 遍历插件列表,并保存为插件实例 + @Override + public void deletePluginFile(PluginInfoDO pluginInfoDO) { + File file = new File(pluginsDir, pluginInfoDO.getFileName()); + if (file.exists()) { + try { + TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 + if (!file.delete()) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); + } + } catch (InterruptedException e) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), + e); + Thread.currentThread().interrupt(); // 恢复中断状态 + } + } + } + + @Override + public String uploadAndLoadNewPlugin(MultipartFile file) { + String pluginKeyNew; + // TODO @haohao:多节点,是不是要上传 s3 之类的存储器;然后定时去加载 + Path pluginsPath = Paths.get(pluginsDir); + try { + FileUtil.mkdir(pluginsPath.toFile()); // 创建插件目录 + String filename = file.getOriginalFilename(); + if (filename != null) { + Path jarPath = pluginsPath.resolve(filename); + Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件 + pluginKeyNew = pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件 + log.info("已加载插件: {}", pluginKeyNew); + } else { + throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED); + } + } catch (IOException e) { + log.error("[uploadAndLoadNewPlugin][上传插件文件失败]", e); + throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); + } catch (Exception e) { + log.error("[uploadAndLoadNewPlugin][加载插件失败]", e); + throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); + } + return pluginKeyNew; + } + + @Override + public void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) { + // TODO @haohao:疑问,这里写 enabled.txt 和 disabled.txt 的目的是啥哈? + // pf4j 的插件状态文件,需要 2 个文件,一个 enabled.txt 一个 disabled.txt + Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); + Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); + Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; + Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; + + try { + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew); + if (pluginWrapper == null) { + throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED); + } + List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) + : new ArrayList<>(); + List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) + : new ArrayList<>(); + + if (!targetLines.contains(pluginKeyNew)) { + targetLines.add(pluginKeyNew); + Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + log.info("已添加插件 {} 到 {}", pluginKeyNew, targetFilePath.getFileName()); + } + + if (oppositeLines.contains(pluginKeyNew)) { + oppositeLines.remove(pluginKeyNew); + Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING); + log.info("已从 {} 移除插件 {}", oppositeFilePath.getFileName(), pluginKeyNew); + } + } catch (IOException e) { + log.error("[updatePluginStatusFile][更新插件状态文件失败]", e); + throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); + } + } + + @Override + public void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status) { + String pluginKey = pluginInfoDo.getPluginKey(); + PluginWrapper plugin = pluginManager.getPlugin(pluginKey); + + if (plugin != null) { + // 启动插件 + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) + && plugin.getPluginState() != PluginState.STARTED) { + pluginManager.startPlugin(pluginKey); + updatePluginStatusFile(pluginKey, true); + log.info("已启动插件: {}", pluginKey); + } + // 停止插件 + else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) + && plugin.getPluginState() == PluginState.STARTED) { + pluginManager.stopPlugin(pluginKey); + updatePluginStatusFile(pluginKey, false); + log.info("已停止插件: {}", pluginKey); + } + } else { + // 插件不存在且状态为停止,抛出异常 + if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { + throw exception(ErrorCodeConstants.PLUGIN_STATUS_INVALID); + } + } + } + + @Override + public void reportPluginInstances() { + // 1. 获取 pf4j 插件列表 + List plugins = pluginManager.getPlugins(); + + // 2. 获取插件信息列表并转换为 Map 以便快速查找 + List pluginInfos = pluginInfoMapper.selectList(); + Map pluginInfoMap = pluginInfos.stream() + .collect(Collectors.toMap(PluginInfoDO::getPluginKey, Function.identity())); + + // 3. 获取本机 IP 和 MAC 地址 + LinkedHashSet localAddressList = NetUtil.localAddressList(t -> t instanceof Inet4Address); + LinkedHashSet ipList = NetUtil.toIpList(localAddressList); + String ip = ipList.stream().findFirst().orElse("127.0.0.1"); + String mac = NetUtil.getMacAddress(localAddressList.stream().findFirst().orElse(null)); + String mainId = MAIN_ID + "-" + mac; + + // 4. 遍历插件列表,并保存为插件实例 for (PluginWrapper plugin : plugins) { - // 2.1 查找插件信息 String pluginKey = plugin.getPluginId(); - // TODO @haohao:CollUtil.findOne() 简化 - PluginInfoDO pluginInfo = pluginInfos.stream() - .filter(pluginInfoDO -> pluginInfoDO.getPluginKey().equals(pluginKey)) - .findFirst() - .orElse(null); + + // 4.1 查找插件信息 + PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey); if (pluginInfo == null) { - // TODO @haohao:建议打个 error log + // 4.2 插件信息不存在,记录错误并跳过 + log.error("插件信息不存在,插件包标识符 = {}", pluginKey); continue; } - // 2.2 查询插件实例 - PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(MAIN_ID, pluginInfo.getId()); - // 2.3.1 如果插件实例不存在,则创建 + // 4.3 查询插件实例 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(mainId, + pluginInfo.getId()); if (pluginInstance == null) { - // TODO @haohao:可以链式调用;建议新建一个! - pluginInstance = new PluginInstanceDO(); - pluginInstance.setPluginId(pluginInfo.getId()); - pluginInstance.setMainId(MAIN_ID); - pluginInstance.setIp(mainIp); - pluginInstance.setPort(port); - pluginInstance.setHeartbeatAt(System.currentTimeMillis()); + // 4.4 如果插件实例不存在,则创建 + pluginInstance = PluginInstanceDO.builder() + .pluginId(pluginInfo.getId()) + .mainId(MAIN_ID + "-" + mac) + .ip(ip) + .port(port) + .heartbeatAt(System.currentTimeMillis()) + .build(); pluginInstanceMapper.insert(pluginInstance); } else { - // 2.3.2 如果插件实例存在,则更新 + // 4.5 如果插件实例存在,则更新心跳时间 pluginInstance.setHeartbeatAt(System.currentTimeMillis()); pluginInstanceMapper.updateById(pluginInstance); } } } - // TODO @haohao:这个目的是,获取到第一个有效 ip 是哇? - private String getLocalIpAddress() { - try { - List ipList = NetUtil.localIpv4s().stream() - .filter(ip -> !ip.startsWith("0.0") && !ip.startsWith("127.") && !ip.startsWith("169.254") && !ip.startsWith("255.255.255.255")) - .toList(); - return ipList.isEmpty() ? "127.0.0.1" : ipList.get(0); - } catch (Exception e) { - log.error("获取本地IP地址失败", e); - return "127.0.0.1"; // 默认值 - } - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java index 0a9ba9ee47..8682a549c0 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java @@ -11,6 +11,11 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.concurrent.CompletableFuture; +/** + * 插件实例 RPC 接口 + * + * @author 芋道源码 + */ @RestController @RequestMapping("/rpc") @RequiredArgsConstructor @@ -29,4 +34,4 @@ public class RpcController { return rpcClient.call("concat", new Object[]{str1, str2}, 10); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java index 6d0908683b..b91146712f 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -12,7 +13,7 @@ import io.netty.util.CharsetUtil; /** * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 - * + *

* 1. 请求格式:JSON 格式,地址为 POST /sys/{productKey}/{deviceName}/thing/event/property/post * 2. 返回结果:JSON 格式,包含统一的 code、data、id、message、method、version 字段 */ @@ -76,7 +77,12 @@ public class HttpHandler extends SimpleChannelInboundHandler { try { // 调用主程序的接口保存数据 - deviceDataApi.saveDeviceData(productKey, deviceName, jsonData.toString()); + DeviceDataCreateReqDTO createDTO = DeviceDataCreateReqDTO.builder() + .productKey(productKey) + .deviceName(deviceName) + .message(jsonData.toString()) + .build(); + deviceDataApi.saveDeviceData(createDTO); // 构造成功响应内容 JSONObject successRes = createResponseJson( From 77b89aad773e021b2a7f9d0c85bdbbec468af000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Tue, 7 Jan 2025 23:13:57 +0800 Subject: [PATCH 077/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E9=9B=86=E6=88=90=20Vert.x=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E9=87=8D=E6=9E=84=20HTTP=20?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E4=B8=BA=20Vert.x=20=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 14 ++ yudao-module-iot/yudao-module-iot-biz/pom.xml | 10 ++ .../plugin/PluginInstanceServiceImpl.java | 6 +- .../dependency-reduced-pom.xml | 81 ++++++++++ .../plugin.properties | 2 +- .../yudao-module-iot-http-plugin/pom.xml | 57 ++++--- .../yudao/module/iot/plugin/HttpHandler.java | 153 ------------------ .../yudao/module/iot/plugin/HttpPlugin.java | 94 ----------- .../module/iot/plugin/HttpVertxHandler.java | 105 ++++++++++++ .../module/iot/plugin/HttpVertxPlugin.java | 70 ++++++++ .../src/main/resources/application-dev.yaml | 7 +- .../src/main/resources/application-local.yaml | 44 +++-- 12 files changed, 355 insertions(+), 288 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 6eaa89dfe7..fb3cf8562d 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -67,6 +67,7 @@ 3.0.6 1.2.5 0.9.0 + 4.4.0 3.5.0 4.11.0 @@ -613,6 +614,19 @@ ${pf4j-spring.version} + + + io.vertx + vertx-core + ${vertx.version} + + + + io.vertx + vertx-web + ${vertx.version} + + diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index d0ed0bcacd..66710ae910 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -64,6 +64,16 @@ yudao-spring-boot-starter-excel + + + io.vertx + vertx-core + + + + io.vertx + vertx-web + org.eclipse.paho diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index 618d09c733..a4bae89647 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -196,10 +196,8 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { .collect(Collectors.toMap(PluginInfoDO::getPluginKey, Function.identity())); // 3. 获取本机 IP 和 MAC 地址 - LinkedHashSet localAddressList = NetUtil.localAddressList(t -> t instanceof Inet4Address); - LinkedHashSet ipList = NetUtil.toIpList(localAddressList); - String ip = ipList.stream().findFirst().orElse("127.0.0.1"); - String mac = NetUtil.getMacAddress(localAddressList.stream().findFirst().orElse(null)); + String ip = NetUtil.getLocalhostStr(); + String mac = NetUtil.getLocalMacAddress(); String mainId = MAIN_ID + "-" + mac; // 4. 遍历插件列表,并保存为插件实例 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml new file mode 100644 index 0000000000..f4ec60d961 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml @@ -0,0 +1,81 @@ + + + + yudao-module-iot-plugin + cn.iocoder.boot + 2.2.0-snapshot + + 4.0.0 + yudao-module-iot-http-plugin + ${project.artifactId} + 2.2.0-snapshot + 物联网 插件模块 - http 插件 + + + + maven-jar-plugin + 2.4 + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.description} + ${plugin.dependencies} + + + + + + maven-deploy-plugin + + true + + + + maven-shade-plugin + 3.4.1 + + + package + + shade + + + true + shaded + + + cn.iocoder.yudao.module.iot.HttpPluginSpringbootApplication + + + + + + + + + + + org.pf4j + pf4j-spring + 0.9.0 + provided + + + org.projectlombok + lombok + 1.18.34 + provided + + + + cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin + 0.0.1 + http-plugin + http-plugin-0.0.1 + ahh + + diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties index 4e1199acfc..44f221cb15 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties @@ -1,5 +1,5 @@ plugin.id=http-plugin -plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpPlugin +plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin plugin.version=0.0.1 plugin.provider=ahh plugin.dependencies= diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index 27c1d19a0a..29c0200f1c 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -21,7 +21,7 @@ http-plugin - cn.iocoder.yudao.module.iot.plugin.HttpPlugin + cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin 0.0.1 ahh http-plugin-0.0.1 @@ -30,27 +30,6 @@ - - org.apache.maven.plugins maven-antrun-plugin @@ -118,6 +97,29 @@ true + + + + + + + + + + + + + + + + + + + + + + + @@ -145,10 +147,15 @@ ${lombok.version} provided + - io.netty - netty-all - 4.1.63.Final + io.vertx + vertx-core + + + + io.vertx + vertx-web diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java deleted file mode 100644 index b91146712f..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java +++ /dev/null @@ -1,153 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http.*; -import io.netty.util.CharsetUtil; - -/** - * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 - *

- * 1. 请求格式:JSON 格式,地址为 POST /sys/{productKey}/{deviceName}/thing/event/property/post - * 2. 返回结果:JSON 格式,包含统一的 code、data、id、message、method、version 字段 - */ -public class HttpHandler extends SimpleChannelInboundHandler { - - private final DeviceDataApi deviceDataApi; - - public HttpHandler(DeviceDataApi deviceDataApi) { - this.deviceDataApi = deviceDataApi; - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { - // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post - // 使用 "/" 拆分路径 - String uri = request.uri(); - String[] parts = uri.split("/"); - - /* - 拆分结果示例: - parts[0] = "" - parts[1] = "sys" - parts[2] = productKey - parts[3] = deviceName - parts[4] = "thing" - parts[5] = "event" - parts[6] = "property" - parts[7] = "post" - */ - boolean isCorrectPath = parts.length == 8 - && "sys".equals(parts[1]) - && "thing".equals(parts[4]) - && "event".equals(parts[5]) - && "property".equals(parts[6]) - && "post".equals(parts[7]); - if (!isCorrectPath) { - writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); - return; - } - String productKey = parts[2]; - String deviceName = parts[3]; - - // 从请求中获取原始数据,尝试解析请求数据为 JSON 对象 - String requestBody = request.content().toString(CharsetUtil.UTF_8); - JSONObject jsonData; - try { - jsonData = JSONUtil.parseObj(requestBody); - } catch (Exception e) { - JSONObject res = createResponseJson( - 400, - new JSONObject(), - null, - "请求数据不是合法的 JSON 格式: " + e.getMessage(), - "thing.event.property.post", - "1.0" - ); - writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); - return; - } - String id = jsonData.getStr("id", null); - - try { - // 调用主程序的接口保存数据 - DeviceDataCreateReqDTO createDTO = DeviceDataCreateReqDTO.builder() - .productKey(productKey) - .deviceName(deviceName) - .message(jsonData.toString()) - .build(); - deviceDataApi.saveDeviceData(createDTO); - - // 构造成功响应内容 - JSONObject successRes = createResponseJson( - 200, - new JSONObject(), - id, - "success", - "thing.event.property.post", - "1.0" - ); - writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); - } catch (Exception e) { - JSONObject errorRes = createResponseJson( - 500, - new JSONObject(), - id, - "The format of result is error!", - "thing.event.property.post", - "1.0" - ); - writeResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, errorRes.toString()); - } - } - - /** - * 创建标准化的响应 JSON 对象 - * - * @param code 响应状态码(业务层面的) - * @param data 返回的数据对象(JSON) - * @param id 请求的 id(可选) - * @param message 返回的提示信息 - * @param method 返回的 method 标识 - * @param version 返回的版本号 - * @return 构造好的 JSON 对象 - */ - private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { - JSONObject res = new JSONObject(); - res.set("code", code); - res.set("data", data != null ? data : new JSONObject()); - res.set("id", id); - res.set("message", message); - res.set("method", method); - res.set("version", version); - return res; - } - - /** - * 向客户端返回 HTTP 响应的辅助方法 - * - * @param ctx 通道上下文 - * @param status HTTP 响应状态码(网络层面的) - * @param content 响应内容(JSON 字符串或其他文本) - */ - private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { - // 设置响应头为 JSON 类型和正确的编码 - FullHttpResponse response = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, - status, - Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) - ); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); - response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); - - // 发送响应并在发送完成后关闭连接 - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java deleted file mode 100644 index 66e0c69a39..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.*; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http.*; -import lombok.extern.slf4j.Slf4j; -import org.pf4j.PluginWrapper; -import org.pf4j.Plugin; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -@Slf4j -public class HttpPlugin extends Plugin { - - private static final int PORT = 8092; - - private ExecutorService executorService; - private DeviceDataApi deviceDataApi; - - public HttpPlugin(PluginWrapper wrapper) { - super(wrapper); - // 初始化线程池 - this.executorService = Executors.newSingleThreadExecutor(); - } - - @Override - public void start() { - log.info("HttpPlugin.start()"); - - // 重新初始化线程池,确保它是活跃的 - if (executorService.isShutdown() || executorService.isTerminated()) { - executorService = Executors.newSingleThreadExecutor(); - } - - // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 - deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); - if (deviceDataApi == null) { - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); - return; - } - - // 异步启动 Netty 服务器 - executorService.submit(this::startHttpServer); - } - - @Override - public void stop() { - log.info("HttpPlugin.stop()"); - // 停止线程池 - executorService.shutdownNow(); - } - - /** - * 启动 HTTP 服务 - */ - private void startHttpServer() { - EventLoopGroup bossGroup = new NioEventLoopGroup(1); - EventLoopGroup workerGroup = new NioEventLoopGroup(); - - try { - ServerBootstrap bootstrap = new ServerBootstrap(); - bootstrap.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .childHandler(new ChannelInitializer<>() { - - @Override - protected void initChannel(Channel channel) { - channel.pipeline().addLast(new HttpServerCodec()); - channel.pipeline().addLast(new HttpObjectAggregator(65536)); - // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 - channel.pipeline().addLast(new HttpHandler(deviceDataApi)); - } - - }); - - // 绑定端口并启动服务器 - ChannelFuture future = bootstrap.bind(PORT).sync(); - log.info("HTTP 服务器启动成功,端口为: {}", PORT); - future.channel().closeFuture().sync(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.warn("HTTP 服务启动被中断", e); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java new file mode 100644 index 0000000000..335d6c95d2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java @@ -0,0 +1,105 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import io.vertx.core.Handler; +import io.vertx.ext.web.RequestBody; +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class HttpVertxHandler implements Handler { + + private final DeviceDataApi deviceDataApi; + + public HttpVertxHandler(DeviceDataApi deviceDataApi) { + this.deviceDataApi = deviceDataApi; + } + + @Override + public void handle(RoutingContext ctx) { + String productKey = ctx.pathParam("productKey"); + String deviceName = ctx.pathParam("deviceName"); + RequestBody requestBody = ctx.body(); + + JSONObject jsonData; + try { + jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); + } catch (Exception e) { + JSONObject res = createResponseJson( + 400, + new JSONObject(), + null, + "请求数据不是合法的 JSON 格式: " + e.getMessage(), + "thing.event.property.post", + "1.0"); + ctx.response() + .setStatusCode(400) + .putHeader("Content-Type", "application/json; charset=UTF-8") + .end(res.toString()); + return; + } + + String id = jsonData.getStr("id", null); + + try { + // 调用主程序的接口保存数据 + DeviceDataCreateReqDTO createDTO = DeviceDataCreateReqDTO.builder() + .productKey(productKey) + .deviceName(deviceName) + .message(jsonData.toString()) + .build(); + deviceDataApi.saveDeviceData(createDTO); + + // 构造成功响应内容 + JSONObject successRes = createResponseJson( + 200, + new JSONObject(), + id, + "success", + "thing.event.property.post", + "1.0"); + ctx.response() + .setStatusCode(200) + .putHeader("Content-Type", "application/json; charset=UTF-8") + .end(successRes.toString()); + } catch (Exception e) { + JSONObject errorRes = createResponseJson( + 500, + new JSONObject(), + id, + "The format of result is error!", + "thing.event.property.post", + "1.0"); + ctx.response() + .setStatusCode(500) + .putHeader("Content-Type", "application/json; charset=UTF-8") + .end(errorRes.toString()); + } + } + + /** + * 创建标准化的响应 JSON 对象 + * + * @param code 响应状态码(业务层面的) + * @param data 返回的数据对象(JSON) + * @param id 请求的 id(可选) + * @param message 返回的提示信息 + * @param method 返回的 method 标识 + * @param version 返回的版本号 + * @return 构造好的 JSON 对象 + */ + private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, + String version) { + JSONObject res = new JSONObject(); + res.set("code", code); + res.set("data", data != null ? data : new JSONObject()); + res.set("id", id); + res.set("message", message); + res.set("method", method); + res.set("version", version); + return res; + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java new file mode 100644 index 0000000000..c1d587489d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class HttpVertxPlugin extends Plugin { + + private static final int PORT = 8092; + private Vertx vertx; + private DeviceDataApi deviceDataApi; + + public HttpVertxPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + log.info("HttpVertxPlugin.start()"); + + // 获取 DeviceDataApi 实例 + deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); + if (deviceDataApi == null) { + log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); + return; + } + + // 初始化 Vert.x + vertx = Vertx.vertx(); + Router router = Router.router(vertx); + + // 处理 Body + router.route().handler(BodyHandler.create()); + + // 设置路由 + router.post("/sys/:productKey/:deviceName/thing/event/property/post") + .handler(new HttpVertxHandler(deviceDataApi)); + + // 启动 HTTP 服务器 + vertx.createHttpServer() + .requestHandler(router) + .listen(PORT, http -> { + if (http.succeeded()) { + log.info("HTTP 服务器启动成功,端口为: {}", PORT); + } else { + log.error("HTTP 服务器启动失败", http.cause()); + } + }); + } + + @Override + public void stop() { + log.info("HttpVertxPlugin.stop()"); + if (vertx != null) { + vertx.close(ar -> { + if (ar.succeeded()) { + log.info("Vert.x 关闭成功"); + } else { + log.error("Vert.x 关闭失败", ar.cause()); + } + }); + } + } +} \ No newline at end of file diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index 5457247e6f..c0bd5f64da 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -212,4 +212,9 @@ iot: # 保持连接 keepalive: 60 # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) - clearSession: true \ No newline at end of file + clearSession: true + + +# 插件配置 +pf4j: + pluginsDir: ${user.home}/plugins # 插件目录 \ No newline at end of file diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index e5ae6d195a..b6492a1f46 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,7 +45,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 @@ -53,8 +53,8 @@ spring: # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: root - password: ahh@123456 + username: ruoyi-vue-pro + password: ruoyi-@h2ju02hebp # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -63,17 +63,25 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: ruoyi-vue-pro + password: ruoyi-@h2ju02hebp + tdengine: # IOT 数据库 +# lazy: true # 开启懒加载,保证启动速度 + url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro + driver-class-name: com.taosdata.jdbc.rs.RestfulDriver username: root - password: ahh@123456 + password: taosdata + druid: + validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: 127.0.0.1 # 地址 + host: chaojiniu.top # 地址 port: 6379 # 端口 - database: 0 # 数据库索引 -# password: dev # 密码,建议生产环境开启 + database: 15 # 数据库索引 + password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### @@ -175,8 +183,10 @@ logging: cn.iocoder.yudao.module.crm.dal.mysql: debug cn.iocoder.yudao.module.erp.dal.mysql: debug cn.iocoder.yudao.module.iot.dal.mysql: debug + cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG cn.iocoder.yudao.module.ai.dal.mysql: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + com.taosdata: DEBUG # TDengine 的日志级别 debug: false @@ -259,7 +269,7 @@ justauth: iot: emq: # 账号 - username: anhaohao + username: haohao # 密码 password: ahh@123456 # 主机地址 @@ -271,4 +281,18 @@ iot: # 保持连接 keepalive: 60 # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) - clearSession: true \ No newline at end of file + clearSession: true + +# MQTT-RPC 配置 +mqtt: + broker: tcp://chaojiniu.top:1883 + username: haohao + password: ahh@123456 + clientId: mqtt-rpc-server-${random.int} + requestTopic: rpc/request + responseTopicPrefix: rpc/response/ + + +# 插件配置 +pf4j: + pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录 \ No newline at end of file From d39e2c1bc4127ffc7b4b17cb9b4a080bc2898b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Tue, 7 Jan 2025 23:21:49 +0800 Subject: [PATCH 078/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-local.yaml | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index b6492a1f46..ebfc4faa17 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,7 +45,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 @@ -53,8 +53,8 @@ spring: # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: ruoyi-vue-pro - password: ruoyi-@h2ju02hebp + username: root + password: 123456 # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -63,12 +63,12 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true - username: ruoyi-vue-pro - password: ruoyi-@h2ju02hebp + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: root + password: 123456 tdengine: # IOT 数据库 # lazy: true # 开启懒加载,保证启动速度 - url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro + url: jdbc:TAOS-RS://127.0.0.1:6041/ruoyi_vue_pro driver-class-name: com.taosdata.jdbc.rs.RestfulDriver username: root password: taosdata @@ -78,10 +78,10 @@ spring: # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: chaojiniu.top # 地址 + host: 400-infra.server.iocoder.cn # 地址 port: 6379 # 端口 - database: 15 # 数据库索引 - password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 + database: 1 # 数据库索引 + # password: 123456 # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### @@ -269,11 +269,11 @@ justauth: iot: emq: # 账号 - username: haohao + username: root # 密码 - password: ahh@123456 + password: 123456 # 主机地址 - hostUrl: tcp://chaojiniu.top:1883 + hostUrl: tcp://127.0.0.1:1883 # 客户端Id,不能相同,采用随机数 ${random.value} client-id: ${random.int} # 默认主题 @@ -285,9 +285,9 @@ iot: # MQTT-RPC 配置 mqtt: - broker: tcp://chaojiniu.top:1883 - username: haohao - password: ahh@123456 + broker: tcp://127.0.0.1:1883 + username: root + password: 123456 clientId: mqtt-rpc-server-${random.int} requestTopic: rpc/request responseTopicPrefix: rpc/response/ From 0af6d5a7581532b4658160a19680c09ffd427279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 8 Jan 2025 17:59:32 +0800 Subject: [PATCH 079/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=96=B0=E5=A2=9E=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E9=80=BB=E8=BE=91=EF=BC=8C=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D=E4=BD=BF=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E6=96=87=E4=BB=B6=E6=9B=B4=E6=96=B0=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 16779 -> 15105 bytes .../admin/plugin/vo/PluginInfoPageReqVO.java | 5 +- .../iot/framework/plugin/PluginStart.java | 51 ++++++++++++++++++ .../plugin/UnifiedConfiguration.java | 8 ++- .../iot/service/plugin/PluginInfoService.java | 5 +- .../service/plugin/PluginInfoServiceImpl.java | 1 - .../service/plugin/PluginInstanceService.java | 8 --- .../plugin/PluginInstanceServiceImpl.java | 46 +--------------- .../module/iot/plugin/HttpVertxPlugin.java | 16 +++++- 9 files changed, 80 insertions(+), 60 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar index 7193d630d82b0166448c05c7550933db82624dac..fa75769049a1e0036436209788e16c216edd4891 100644 GIT binary patch delta 6325 zcmZu$1z1#DyPlzAkd|(S7`nSlx>Ha_y1T(mNl1tcE!`pAAqYcBhe|7)Er{dM61EH>%j^_|4!IKVl3&vWrh{;<< z{kbe1pzV&%1bK3PZ#4qnT6N zzN`R6&_EjJg}$rxx@%@j8Pz;rKJ5eNyMz(VN{Yraqrd#fx+_~$yo`i`zd`OVdbibo z8Y_l*7(2EKvdB#Y+&@7>cg+`nVfl-hk5K2cv>Dz(MC2VTQp*~B@KnE1EWzD4P4=F2 zlKU^c&Dpz=T&ky7uh?8w0nhjGkId^D+)V0Zy*n^>+>W*jmM_x=w0Lx+kjpwh?;o<| z{sR4HvQIh?ykG{jThIlwqaYB>gMtdMFj`CpVht>g0tz%a6qunYp?rC0@+j*gn%bWr zh6rcHqWP;P&g~6kw+Iz`|J7xOdD+W zLD2|W)@aKqb)BfhSNGO6J~O3zL+k4_%Dz)`1!<=j7*wV7IhgKP*rt!Gznk*)I0sH| zeyIUBCnGnjS3Kb(UQ%%&7@3iCrJ{(^L7Z&)i1ymp;7yBGqEsZ3b`al+8#<8S1ZDtVD9G4ITf4MPoE zHU=KK1;pRxrK#lXLk1cI^V*<}@kMO)319WsW_=(l#qs9z*wIS9ZjD2lu=mfWHodM+ zX)`5{7Knz?A?fN;GE+qH!z2mDCx-L^D%kohwXaoO4I0dmcab zO{S;wbky|eyjBJCY7(7%WeKD)qjSInc^A}@y!&3wC`lMT!&Zi4QROJ9@)l3f3OVdd z@AS|yi1prVw<*%b(GI7KwhsSsVs{V;5*iMfRtw2!2>!MqyEtYrIu^cMp>J<6P^Gi$ z329VupEj_#>=b!TXOZ6+g%L`2fF~*JTb!`5Vdtf<(`5<4u?lo&FJn?tY5^QNsf~RN zv#!kT;FzPG2hN=NxZ_RhQ&f+oPr}*w=Wt~U$#Ve%wER(k@!9Qp49hZOs zX=ubEL?R=3{XsCk<>dg$pgJI3yG}|i8}C55oM}!xKag)oE~Uv7R_)SIpr9Tcv#XTz zbU5Swqaz+qm&?z|#Uz6AY6c1#NDZRG>FZ}d1On}>&Tja9%#f0$pSf%LQ{KZm9}ys2 zN~pDFk_@q(hRI5x4DvzUdkCsY4cw19gQ)}Q%usMH`7&+~HL7Q(=`QnEY zA$eNjX^UX_3;H7ldo%W$Ic7Y$Hw6pFXua~6T9`549d?YN1T@6;3kg(BT^hx-UxxBEhGJ}#=!5`T5kGF!fU1s0e z`fKDeos6}Llj^fh0kBC&H0Fb_X_RSy&}Do3KXeJKrWXW}vMZXj@f z-nH|Uu8qfXW9PCMs5-26zGjPC@YL@2TRPLylb)m>Zy5fn|3I*L5qFIsZQQ4phxqQ3 zyT)DjN!w)hK4%{4Zwri#_dqV+cvrRDQ?78&L61oh+a~CO-*_EtpAO>BXV&Q!00$n3 zFBN~C9Yep`HVI~9$+p&t(*sLv@PBVpyu?VDwKfR}~EFYn`V(mh$bgm6I2E9ru zYW-bW?}fKdWdU#@(+wA2^T%JkudWeav{=&{;r*ccwd#eV?ZzHjj{63&$n$04hQ+iZa}(k~iZl48T)E7Z)I|!y{-yi?OI-|MsmME#HkdJhmpTFkB1F zQmcWa(Hj^edkLc6JN8$%>X2l$Kdpqy1cpS=|ESBaSp;-b_ZCB%zuMq0Erp!h798g_ zCc%FD=MGC<2ql3TRx@{y*k-z;Yr4mC>{Oz}RxJAN<>z;ue=-b!sR`%zKwdIQ90n~e zspH;`Y)(=;yuoDCaQ3N36mM3N@E4=F1}`Q365D5!KbD{a#Zy;ww9rYb3w-r!c9IK< zU~|kXGl1xqt4RJJ;V$bzWslNk4Ou0gGx_!CjT?qx!>^A!Mnv68Yf(+sFEX4qNn0le zM|2%=MyE3j;(n#0-7E8|#DH@JL4EC!tg^QJt#$xy)Kjcx90fRrY(SLZThL;0}~FGSJFUvXKe(mZ=eSurxo>f)AZc zUk=$ykGD#tiJvo9Wtrn&h{o0k=pYkLX)8twzvLdl-?ka%U$;ang>k-frltmBm(d>N zPYP$36n@QBXt--TK7IX}xnh-VDn&9LU(C}Mwxu6_}N{7j@-=={T%?2x0?&WAUE z!&feloXT7RGnppGa{=r!NP0QB@uOEXgq#P#($!RuvKJysKKb^6(nMn`t1eZIl(8spzoxlN?dg zn2mlBB!VUSf=3FN$VlnS*0vH09Y!-C8D^w;{3a_Q2x5G>41wm%x(H4XM&GHzHJ&wQ zsrHr3uvd=nh^CH7YMwQ=$syUm<(WvYJ!~-cdz+E9J@ibE3tp5s(io?3`A}4{6WSsb zXw*#SlG0D}S;zl5e8|_ow##{CvfqiTJLJx{hI#JQ9%WU!YsfQ|@@DuVS#TO)nBtQu zK$}Z>b^4p;Ve&_bgXxmpJ|I{)Y&-V`3u3R*aVuV%k+XMSR*s-G++ak~Rb4)6_ozh4IZZe3(RPDlhH!<;`#L7-1 zdb1hZDEMsbLgGw-JKq?{DH_=18|ggIvL8gqF(3sga@3u`z72SLG>(O-qspsa`)zsE%Y(h<38eEg-oqgBS3uhM0`B*Nh0VQ0ZtgPt)o6*I<(H;V_j|~*xZG{H#UCPXe=DC^-lwHLHN&_Pe&i>U)YWI$%b-XwSg|=XP z9|jp%b5p!XDn>OgJ-&;Ie9hX#n>;I)r7(pU}%@D*qf4HmhtKA{f~RNTKPqpYMU{$&CKzx^34 z9Ch@R@YJ(LQ#qRVT<7pK`?>$SVsF(S`Jhr{c6n^->jU{!-#s>DUNZIJ$Ex9`YAAE9 zlesB&FthWki|U8d4Xko6D(sw^8w>@iYEyzaRUVeL^% zYtJfI;%lb{#y#7)G`)rro*g$v_l;d(AR*)Bw6#>Y%>`Uc6lub5KUlhsVV478LNb!( zWf1vo$>kqR^IxBhukQRd;=;nmy5fqI+`H|K=^5N7!N6 z)Mj;J0%i385l<-7T~*s2>@o7r$-+VJ6T}LMyZ+{HfN|vBg_H{5uMcaYf^$O$nXBJH z%{U~TtCKutO*xY)oGjyn#5GUk=6Y~|Vqh%r)8` z0u(*0@0NVV`en;4XA&;{#XWyS>Xgf<4~nL^vqNyNcf_EQmG$E_>)mV86Av3NBoFs* zG1wrmJx7*70WW2WPj7F`5j9~iil#Qa5Ktn~Ej!jCJ`=sl*WT!4UhPi~Qw$)J5~5L* zed3R0ume-{TbrRgL+89MrVLSAMc@u=R~CM>c+`s{Vx|RX?(|nGfyRpp^OqZp7gc)~ zV^w>U>1Q296l#ZST6`je6Z+A7n*!LU+Ytani2Bzij!gCmaXL_B0kMh0xsy93qTa7Y z56+1nus1TKFpYKaDc`T|VOD7>KaYb_n~IdmuU%pq;X>wzE--wCM#fuI1avn%zd@oa z^kW=$Qh3bNWmYl8$e%jHa5pzu2P3!#b7+;58m_p$KOm_uKVI3SooO@=e@{At^dWDB+L>aFu|7?NyT#rB$WB(5q^}Rx2^O!h#*>}Zb42|cl&tRfZoplu-h3x!Kz_%y+<~jia zlppa*kP8KSf>J187c%=p4#Bw=3a7>Zf#wm3Le>C2nv-cFMq)=7X+V1gLVWSC04@?M zbjel{3E(*vRyTzSp=OhH2DkyPH)>7BI-ozBOyreSr}7rSkvQ6l82-@e17RS^7z zc4I;&Q}qKLy}!Ay#6xp(2h-Qvg^l}7nl!U{9TgrNr&TUH78&`)dnvqcQ7&cnOzgxTDHAWU-pg*CM~A!UG9N z{btebF&z1T?ld`{dv7k-G7cRg$XX;aeVi%X|Pw9m&6$zK?0| zlH00xFRHN<>uc1PE=6cbiuc*1?fdicUJ19d*VwbbUWbKrtL>@B0mk)mAkC>ANS)MG*R`m0J=p7el7?f zcatt0#@AeK3kH-)CcPt@=C7{(RDX=O`cHp#?r;g7*R}=ZTDHZ0h5Wozx5CqSGIN;p zeyFus{my0J;me^s8VA$1{QPr@9VUmVMDHH+s{OjYA&ZP8BhC3T)Ag-Jl9cW12>-T% zAn!NT809eOqN}4=uxsChC;P~xZ+#{pBb17``CCu?Q~P_+4_gVll}+PszZ!@XAti*K zI0Wd&8{mh4W1vT)BilgSv=@s8o2ub2NV{hcOMO5Y) zH&1Hjk-SJ6%Zct@k38|Wo7fLMmDAG%qv4UE|0VAtw#7yMt?wc@Bt%fk=0gcuayBps zWaEB&LII=wm2&Gzk)Q>uA!;S4QDJW+Xwf{uhzN92#FYdD#o$QNq7|SRYJ|Kb1ofk- zbIZI#G4zP1e==FO3?bSr14XR-$?V=TdMJh-kw}G);FtQNmaG&lTKbQSk zH|5s5@Ryeuu_lFw8V$uVqW@8e038>l$8!HC@BdL`Z&&88t2Dw1mB=ake+AnAF7dB( z+F$v%SQ=4-k1B!rM+t2*&)qD1a-r`Dq^n&uyTvPdKd5rRKDK#`(J zdQ&_E5F0nSM-D76Dw*gvG$ngOLs$@iJ*F=oea{`lFsv(F_Sd`&l1sHmB-dEX5&W?5 zad?)g?XCO1M^T66az+scG@6QOP7HMWV*M;@l6 zy^a{KP#~B77?t+=z;K~DZyZ?6J1;M&3;M=J!#~crq8k>mrx;VIxMy}|lo@9d|9eKA z`F)ozb)-4(XRb^G@}2Xv)#b6h+@-suFADY$_kiZ?>o}halc}AFi%YW(ybsna?qlaD zRpTNODMJ_o7II`p6hjypMcy7ofFfv+%Ky!_nR_DA;z*T?1lg9R?TM%)V`NK<#`2Up ziW@NB%HDRiWp1sd0Nif|gdr;-`K-d{Q)rZ`8jhJH-qFLS;n@Gi zAXICYPM?*rEzBNm-&(SFQ*p{4D^gZZxonh8qq=k|%o>SdrynVc)VL&^#DWzo3$8!j zzR#3ylCSf?jI1}cl>DCPjU7rx*7tO>F)>o>6pF+*RNdbvHT2rJXd5(jebP}aF~Q`O zspr0F()#8ilMKXTrsuuLf;BjEd8m>|Ypo7?A816&{n^kjRh3c(i!ihaEez%riboR} zGR+3;9U@%k-Zyd2voXNs5^)(i0!#DsL#|dl?sq7oBpCW&%)V73&#I}NBmI)xgxoV9 zddV`EPjbpH+G*z0pjk6ab`hFKgoC5%l0@TqO=7CJGVO#F+J{e1sg`DgQq=4{wD$@? z$)J=d9ia1%4VEnX^4Ox#a-EYV>Yyg+BmRhUhSWx>yoR4bFxrRx#bp>Oj0Lbo3+#QH za<7`dCpji#$0#$$jQ>!LO9tUdaJ+e0pXYzn%xD-F!j<0kN^f><>NxUilnWSd#|SCk z)d62ADBV>GXcAgZPUDtoPU%qIMwOTD%E44XtT=FC$~hNlwVQBsrdmbS%TY{3DKbb) zRzOHd;o3P<8oz2js8_1j(ZtRu+c>~DE>CZrajU27%u}y+8Z7HYCpP5ebnAp0vi{`) zW`&(QvK4e@4mS`=7Sp;%hXKys>?`O)ddHLgZJB?t$d+h8J2KuAYhP1)*1ct}+P>B{ z%E@qOB@QH{Lf|z+BYcQHi702uTckraR>z^plpc@p>p)yVq!SLAPka^{(`9B#nsPs* zNUpXbrb(s8ZPA8d=<<_+hQOqPuuzxRVO*slm1{tyF&vAeP8}}ax;fZyXJp4zRlh}x zJ_zaBlk}=){muqG^Dg8UQ@us`>&^c8@P#jNA%5Izw~Jcn^PBI63}Q`ij@IBCRX9JP zbHjV)hG%~B3q@R0lC>P*E(V(KMCX4xAdIhW?G8hY$57Bukwq0~6{6V^7ibk=tn|Hw z>rPKFDz4M#{y|x(a>fMaz?zQL4XMyA!wGd5*`*UjE20Ox0`9kPDLO&H73*Litpz<}(uE}!XDmuLg#=~X zk+;N3RNQ5(Xuh*%a$oZl2NJGxBI>*{G@nA zdoX+(>Hk9VxXUOsoqBG@ol%KTWuQY+H?C5yX^Gd;3a?t)NOjp$zHCs=yd;p{sBohn zYGGXeC@gCux9ocKRk$sqpfy)0j3m>HD5b{rmn0n{h+H>l(SI05v*!eM!hxDen~SVqHn>Qr-E&Ud2$7rdw+sIA)W9R>_`= z6Q+o#zmy&ka;+;`J%3;9=R>%TcCJXosI2t)3{Q>QK{$jO934|RJvpjQma`W*sBFk- zFyGEL^;xkwm^)}4{hOhHyge&rdD+ZM^;TX*%T}9_ZkO9n{>0(8q<#|B<2^riG@=ZP zCBK@sEXu?4tn#or%o{rDx8%p43wJ5Q2rzG1-g?3AF?s56mM8CtCL3ZpSd*K^uFu8w z=GlBhyjS}i1t#+gd98ju8Z9Llj};SJXUoXfWnEUDYTuog!rYV?RCWP>ujJ*jhWVh? zb9Rm~j&oY6)qoPi@pV1jWH%T+Ni5Z5F}QMhVKR05oh@91iL=gZlEuePc~-KOWGgj$ z=Y}76gL(dq1lqY0UJ|FaiCZY){=#bHxfUXns7ckMN#K`A+EPF;F8ghpgM?eR!dMhN zRZRvYJK=PLIEVE1e6i%(D$(|gUa2H=^JmY+wAy{It1>v#0lz`5mJb2{%B>)4SXO$r z%^^L7wAau)1LAW2TKFOC5z#YyoZgXT1ph-~1@eKfoLbB&oI3Mhii3)BtBWfM70|GN zN#4as2R#4t*mE!41nG?#OHC25Q^2q%SE4{FvUiC_Q^j3yOyy!I2TJ^>1Kv`-ht+Zr zGvNwXlgoJh$$J#RrtMvCN2Z-o@y&@E^8VNrE!6=U7ChM-DMbY~#hjWwGdp>hlwPOP z;2NenyCGn@=-^iaIM0vVzVu81U2P8e7yD z*urm{z;(5Yw{`_3(t6U#@*kkCB$ClN7rY0gG3rFewEFL@BU+Of2I8cA z`OJp*HU+a+K77xn;2rH`IVe5oT^HT4IC(R9cu_Q~E?#+dq|1b)gGlBDec$RG>2+wS6HHwBaaw~?oJ5`-y0ZbapG!1*3wQ2cP`R9$VX+E#;1$R@a3ve7(VysMU+%q8rT+N zz8Xo~mc)u$@>wHgoAv+=_rgLnwuQGuPOhwbYbM{5H{OoNl3N9 z5#iR|+1cwc&O&i+FKwz^MZZhEemU`xnD`4KQ5C){Ok+6A07T-V$U0VfO?JLBN?_kX zdP-I|HsnEWTv9}ok#}O9#v6%uY1sK9ytAn3`0Wn$c_w-TZ&M)L!>TN8G)Een2QdAk zKk-BNhhy8@(#8kG$C6p;yvKazeZ^SCh*9yNcf`dj274w(7mFME{~Uzjo?Z#4_3|!_ zW^+IQKs5{i!1?dFRmIND(%ON7^{<0#wuY@TrYM%6IHLS=uNIbMQkaVQG6G^5%`4Vm z=`vawH7G7WJa_7oc=Pv**@?#Y8?V{AIIOLG*8P(BSz@nKBB_qAHKDTG5!SE$XK$9|Y;fJsGEB76wAbAIo|B#aG5 zCS^e%U@K!IbJ!Yd6!=hw1ii{>WQyW}E4nm*)eYiaR`o*Wh7axjGFN8HKQiwWMdE>> zVeh4Q1qEhkq(ijV(tq$QAICm;+0^{@u3y1g-lRxONQoI3Y6zDT5A-^1zVK|;nQ z)ZKxjswu(`B+5K-iDFb2#kS2dzBbsL#N*ZnxPb2%l#ZBV;5CORy0X53dD--#+4oH! zUwr~eCX>J=N_r9DUv&`fbr$o-elMC!FMD@Qb#+LDIf4p@A$LKoM#Le8+M2H07)x$S zY4o?hmF8=3F1l+b$PL3n;@_n35ml!)uRjL=Czu1Nb75sSrtK1~vRSx1=aN(1*$!%eXJ zvLw43(YmmK{NjR7E9Wgk8J;fN0@Xt-nVuHZA{{jFwM+E@b|7TPGy`{D9USsX! zK7KNQ8ios$4rGml9c_@4Dx}hkrX?deaSoK zhovRiX0{+F$w#_l-T z){1+zk?chyk{`Uw7O66#g3f|9ij*emK??g;x?Zoyf>liwR{C zb?=q#e^l$EtFjoF8ATJXH%%>LLfYmOCvA{n%AveP>P#(Yu~|I5@Tux@!I&HJB>IYz zGTt!|UyMsg)lKIihG)nr7CGKBpk=n_lk_GiIEKwg1Xp}<8g67`s^HUD8XA^z$fu-nvQK5l1%=)l^c+fXMk*L#L(x)O;M@eCvtlcWV_-e6?)~L7Ld?1NDc$ZW0q%+h-2KFPdx>?Z;x%pr# zEtX7l^xb1@@Og%kH@t2O7vhUn521rADg` zN!^X6qi1w27`DU+KT#e{zNaNzxhiOz$rlCSW#8IECPwPsW~8li*k8ory)B5;|EYe) z#E~y5;KQc(jS5$Q;(*aQ6olzXTn+UhNRt!VZyo_s`?6<;vwU z0&RZueh))C2e&y>re|%&q;y>==;El9i_zX+ku&*`MUPaka2$_}br>}=!&GVMhEyk2 z$|66tiY%wS=8+^n{KAUSp zICq#Q;0AY&IYC~7Rqd)Ub}PlB$F)Pbl})jZdWH&~xTr72x==@pL2R^Y&&prgZj{4- zCx#tkY=|f9a>muN4zv4(TO7sg%uhLwwtUP9Inh0AHb2@5m7J67{5Qy($Y=@qa1l!g_o z6ZW_zOPgSwI`1)jRja(hAC9&rMvmXDRAX?oa6_#tznH1A&K_7SIJ(ojt!$*Ep&ZC* zxv0z2Lse2|YGP(o;O! zy7q`1DMrU>q+}n4%lxq8oLJ41N=$8#C?vHRt1n}WxKDM0nDl~RO$S!-Cm`Du!bf!q#;G_q30?xwz0 zMsw&9c7#Kw6l8S&P1NrUA|>K%__m~SN1CGkCKIB!*d!r}bVgzO`#*tniQg{or#s*w7xBaUS;|vXf?{ zCRE-F!~ibxxE%ZqF^t1QtEaDa}#w zr*+UaB}r|oF;?oSac{Jvb5B31LeeQ$yf9t^RwU&ut@xyFMNSL zh9|AU?}G+7RFq@!RN6kNn&Y ziDRID7VQlD&*F|j=Qo?)sRrLb01W_yK#UlT!NUkehO8JYTSrPOt2s)?#{8@7ReN>P zN^nh6IXq55*c*YK_zSzNWj(TXrPJvaX<1v&ns+X<+BK26Ygk@&`D(0P%wJ475TS^GWneOnGQXNm(1G5pCO84V5^U5eqz-LHCLe#2l=J8)3wOCQ1 zmP6I=7X^)N7Uc~>=Iy5vuAH#9Eo!xf%H$~NZBZzQKRur0ayts&-36-AuIWP@{zvo!n1BQ zrm-68@tT)pP{ua`Dw?zs{J1{~q`@@$t-mio>$F zpp1f^HNw6Osd;Q8MymI{o}DafuvFI7cw5^DxAu1BY|brBNj zU(d;5wxUtwKU^xFhbv|wd@>%EOEfI2LsXtkJj%!68hM$dz${i6B+YMZR&3}+LeGXB z>~YA1lm|Ij(_XgCkP^|v*&qZPdm`yZ->gyfk>5E5r?4+jkCGCA;(G-;1*0XY55B+E z(TL|f8TWI=K(GEv%Elir`=Z|?()m{s#ZG*QJ2h~sD{GkN^mOf!1jq4YgQ5u4Def{Y z=_=|%_A8Wgi~mQ_&+a>89a6RDfTPd&PBgY-eL9W8NfIUWEx1(eBw38$yeXj&%i3J` zEMN8rNdT~-37;arQ1^}~%5rpM4xcmG_9SO9!eRb%Sw8+v zRg}CXfZ+1m3a*(q_n51#pdfKwlS3A-dzvd(m{6hTi#|^dKB=?}I-%~Lv;^`{~faLNi zfRTsg5rSCI0297StQ&l!$Z}B|6;V@S+E<{$beqp(!9LKAujk|D1;S}fNKvPOjw=zG z>{@}?Yzy=TOP_4Ly@P&QLR%V^rciJS{bFf^XNxK|qe>`XNj~rWkmafoR{-AnD#a%= zX-lixU(FOSt3>-^vkTXl!h-szJ_u=Mb9q$>I#!djT146z5F(HHONF@JTh{O52AO`@T0Zv3SpMiJ~ zR1T!)rzs%^0ci5sLITm}Kp=T$CjLb~{UHBWd3HqvJpqZH3T!VR6@R(6KDjIWWi`S? zggic3pYlQGBmgCVAoU;`r2i!S)Y3XV#9^>J2TNb`|il6<%@aZc|`vR8%XdQ9Q%rr;m=UidcPzi z%DkRPAv_cSfd3CCTcW?G4jY2T^Ly%ucnCm(6u+%+c*uVDdGSN7TIi|IZQU1HXr?pyt3Q@oc`Nc!{yd4O52|)F15I)}Dn(mXf#`bi>kV4XU{~FSNMFa@^ ztDpfMAMx3rfI@!^=XoRk`#1eW35fse@Vu06&kkY`CqC5QiT`<*{s&nUaKZfq)Ih-l P-~qDX0DuLa=f?j77 { // 1. 忽略租户上下文执行 + List pluginInfoList = pluginInfoService + .getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); // 2. 获取运行中的插件列表 + if (CollUtil.isEmpty(pluginInfoList)) { // 3. 检查插件列表是否为空 + log.info("[run] 没有需要启动的插件"); // 4. 日志记录没有插件需要启动 + return; + } + pluginInfoList.forEach(pluginInfo -> { // 5. 使用lambda表达式遍历插件列表 + try { + log.info("[run][启动插件] pluginKey = {}", pluginInfo.getPluginKey()); // 6. 日志记录插件启动信息 + pluginManager.startPlugin(pluginInfo.getPluginKey()); // 7. 启动插件 + } catch (Exception e) { + log.error("[run][启动插件失败] pluginKey = {}", pluginInfo.getPluginKey(), e); // 8. 记录启动失败的日志 + } + }); + }); + + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java index 4f1d5e3e29..374e3856a1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -31,7 +31,13 @@ public class UnifiedConfiguration { @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) public SpringPluginManager pluginManager() { log.info("[init][实例化 SpringPluginManager]"); - SpringPluginManager springPluginManager = new SpringPluginManager(); + SpringPluginManager springPluginManager = new SpringPluginManager() { + @Override + public void startPlugins() { + // 禁用插件启动,避免插件启动时,启动所有插件 + log.info("[init][禁用默认启动所有插件]"); + } + }; springPluginManager.addPluginStateListener(new CustomPluginStateListener()); return springPluginManager; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java index 2e920e32cb..3a1529674f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; @@ -66,7 +67,7 @@ public interface PluginInfoService { * 更新插件的状态 * * @param id 插件id - * @param status 状态 + * @param status 状态 {@link IotPluginStatusEnum} */ void updatePluginStatus(Long id, Integer status); @@ -80,7 +81,7 @@ public interface PluginInfoService { /** * 根据状态获得插件信息列表 * - * @param status 状态 + * @param status 状态 {@link IotPluginStatusEnum} * @return 插件信息列表 */ List getPluginInfoListByStatus(Integer status); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java index 8e1fae88ab..4cf922c20b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -102,7 +102,6 @@ public class PluginInfoServiceImpl implements PluginInfoService { // 3 上传新的插件文件,更新插件启用状态文件 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); - pluginInstanceService.updatePluginStatusFile(pluginKeyNew, false); // 4. 更新插件信息 updatePluginInfo(pluginInfoDo, pluginKeyNew, file); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java index cd1d5a6547..9c360606a8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java @@ -37,14 +37,6 @@ public interface PluginInstanceService { */ String uploadAndLoadNewPlugin(MultipartFile file); - /** - * 更新插件状态文件 - * - * @param pluginKeyNew 插件标识符 - * @param isEnabled 是否启用 - */ - void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled); - /** * 更新插件状态 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index a4bae89647..4a62dbcb92 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -21,11 +21,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetAddress; import java.nio.file.*; -import java.util.ArrayList; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -119,44 +115,6 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { return pluginKeyNew; } - @Override - public void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) { - // TODO @haohao:疑问,这里写 enabled.txt 和 disabled.txt 的目的是啥哈? - // pf4j 的插件状态文件,需要 2 个文件,一个 enabled.txt 一个 disabled.txt - Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); - Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); - Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; - Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; - - try { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew); - if (pluginWrapper == null) { - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED); - } - List targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) - : new ArrayList<>(); - List oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) - : new ArrayList<>(); - - if (!targetLines.contains(pluginKeyNew)) { - targetLines.add(pluginKeyNew); - Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING); - log.info("已添加插件 {} 到 {}", pluginKeyNew, targetFilePath.getFileName()); - } - - if (oppositeLines.contains(pluginKeyNew)) { - oppositeLines.remove(pluginKeyNew); - Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING); - log.info("已从 {} 移除插件 {}", oppositeFilePath.getFileName(), pluginKeyNew); - } - } catch (IOException e) { - log.error("[updatePluginStatusFile][更新插件状态文件失败]", e); - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e); - } - } - @Override public void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status) { String pluginKey = pluginInfoDo.getPluginKey(); @@ -167,14 +125,12 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { pluginManager.startPlugin(pluginKey); - updatePluginStatusFile(pluginKey, true); log.info("已启动插件: {}", pluginKey); } // 停止插件 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { pluginManager.stopPlugin(pluginKey); - updatePluginStatusFile(pluginKey, false); log.info("已停止插件: {}", pluginKey); } } else { @@ -208,7 +164,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey); if (pluginInfo == null) { // 4.2 插件信息不存在,记录错误并跳过 - log.error("插件信息不存在,插件包标识符 = {}", pluginKey); + log.error("插件信息不存在,pluginKey = {}", pluginKey); continue; } diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java index c1d587489d..1d6fcad92b 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java @@ -5,12 +5,15 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; -import org.pf4j.Plugin; import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPlugin; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + import lombok.extern.slf4j.Slf4j; @Slf4j -public class HttpVertxPlugin extends Plugin { +public class HttpVertxPlugin extends SpringPlugin { private static final int PORT = 8092; private Vertx vertx; @@ -67,4 +70,13 @@ public class HttpVertxPlugin extends Plugin { }); } } + + @Override + protected ApplicationContext createApplicationContext() { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); + applicationContext.refresh(); + + return applicationContext; + } } \ No newline at end of file From aad05817773d465c5fc335bc914c04ac0ad011c7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 8 Jan 2025 22:36:38 +0800 Subject: [PATCH 080/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=8F=92=E4=BB=B6=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 5 --- .../enums/plugin/IotPluginDeployTypeEnum.java | 5 +-- .../plugininstance/PluginInstanceDO.java | 7 ++-- .../service/plugin/PluginInfoServiceImpl.java | 2 +- .../service/plugin/PluginInstanceService.java | 2 +- .../plugin/PluginInstanceServiceImpl.java | 36 +++++++++---------- .../module/iot/controller/RpcController.java | 1 + 7 files changed, 27 insertions(+), 31 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index b7a0b8667c..0000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "java.compile.nullAnalysis.mode": "automatic", - "java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx2G -Xms100m -Xlog:disable", - "java.configuration.updateBuildConfiguration": "interactive" -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java index 263873be7d..11f8a6ef88 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -13,8 +13,8 @@ import java.util.Arrays; @Getter public enum IotPluginDeployTypeEnum implements IntArrayValuable { - DEPLOY_VIA_JAR(0, "通过 jar 部署"), // TODO @haohao:UPLOAD 和 ALONE 感觉有点冲突,前者是部署方式,后者是运行方式。这个后续再讨论下哈 - DEPLOY_STANDALONE(1, "独立部署"); + JAR(0, "JAR 部署"), + STANDALONE(1, "独立部署"); public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray(); @@ -48,4 +48,5 @@ public enum IotPluginDeployTypeEnum implements IntArrayValuable { public int[] array() { return ARRAYS; } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java index 507b91f2a0..8b0ec95d14 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java @@ -33,19 +33,22 @@ public class PluginInstanceDO extends BaseDO { */ private String mainId; /** - * 插件id + * 插件 ID *

* 关联 {@link PluginInfoDO#getId()} */ private Long pluginId; + /** - * 插件主程序所在ip + * 插件主程序所在 IP */ private String ip; /** * 插件主程序端口 */ private Integer port; + + // TODO @haohao:字段改成 heartbeatTime,LocalDateTime /** * 心跳时间,心路时间超过 30 秒需要剔除 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java index 4cf922c20b..37b6328450 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java @@ -100,7 +100,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { // 2. 停止并卸载旧的插件 pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); - // 3 上传新的插件文件,更新插件启用状态文件 + // 3 上传新的插件文件,更新插件启用状态文件 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); // 4. 更新插件信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java index 9c360606a8..4df9b10319 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java @@ -11,7 +11,7 @@ import org.springframework.web.multipart.MultipartFile; public interface PluginInstanceService { /** - * 上报插件实例 + * 上报插件实例(心跳) */ void reportPluginInstances(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index 4a62dbcb92..65a6cf32ba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -41,7 +41,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU public class PluginInstanceServiceImpl implements PluginInstanceService { // TODO @haohao:这个可以后续确认下,有没更合适的标识。例如说 mac 地址之类的 - // 简化的UUID + mac 地址 会不会好一些,一台机子有可能会部署多个插件 + // 简化的 UUID + mac 地址 会不会好一些,一台机子有可能会部署多个插件; + // 那就 mac@uuid ? public static final String MAIN_ID = IdUtil.fastSimpleUUID(); @Resource @@ -53,13 +54,13 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @Value("${pf4j.pluginsDir}") private String pluginsDir; - @Value("${server.port:48080}") private int port; @Override public void stopAndUnloadPlugin(String pluginKey) { PluginWrapper plugin = pluginManager.getPlugin(pluginKey); + // TODO @haohao:改成 if return 会更简洁一点; if (plugin != null) { if (plugin.getPluginState().equals(PluginState.STARTED)) { pluginManager.stopPlugin(pluginKey); // 停止插件 @@ -75,6 +76,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @Override public void deletePluginFile(PluginInfoDO pluginInfoDO) { File file = new File(pluginsDir, pluginInfoDO.getFileName()); + // TODO @haohao:改成 if return 会更简洁一点; if (file.exists()) { try { TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 @@ -82,9 +84,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); } } catch (InterruptedException e) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), - e); - Thread.currentThread().interrupt(); // 恢复中断状态 + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); } } } @@ -120,6 +120,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { String pluginKey = pluginInfoDo.getPluginKey(); PluginWrapper plugin = pluginManager.getPlugin(pluginKey); + // TODO @haohao:改成 if return 会更简洁一点; if (plugin != null) { // 启动插件 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) @@ -143,46 +144,41 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @Override public void reportPluginInstances() { - // 1. 获取 pf4j 插件列表 + // 1.1 获取 pf4j 插件列表 List plugins = pluginManager.getPlugins(); - // 2. 获取插件信息列表并转换为 Map 以便快速查找 + // 1.2 获取插件信息列表并转换为 Map 以便快速查找 List pluginInfos = pluginInfoMapper.selectList(); Map pluginInfoMap = pluginInfos.stream() .collect(Collectors.toMap(PluginInfoDO::getPluginKey, Function.identity())); - // 3. 获取本机 IP 和 MAC 地址 + // 1.3 获取本机 IP 和 MAC 地址 String ip = NetUtil.getLocalhostStr(); String mac = NetUtil.getLocalMacAddress(); String mainId = MAIN_ID + "-" + mac; - // 4. 遍历插件列表,并保存为插件实例 + // 2. 遍历插件列表,并保存为插件实例 for (PluginWrapper plugin : plugins) { String pluginKey = plugin.getPluginId(); - // 4.1 查找插件信息 + // 2.1 查找插件信息 PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey); if (pluginInfo == null) { - // 4.2 插件信息不存在,记录错误并跳过 log.error("插件信息不存在,pluginKey = {}", pluginKey); continue; } - // 4.3 查询插件实例 + // 2.2 情况一:如果插件实例不存在,则创建 PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(mainId, pluginInfo.getId()); if (pluginInstance == null) { // 4.4 如果插件实例不存在,则创建 - pluginInstance = PluginInstanceDO.builder() - .pluginId(pluginInfo.getId()) - .mainId(MAIN_ID + "-" + mac) - .ip(ip) - .port(port) - .heartbeatAt(System.currentTimeMillis()) - .build(); + pluginInstance = PluginInstanceDO.builder().pluginId(pluginInfo.getId()).mainId(MAIN_ID + "-" + mac) + .ip(ip).port(port).heartbeatAt(System.currentTimeMillis()).build(); pluginInstanceMapper.insert(pluginInstance); } else { - // 4.5 如果插件实例存在,则更新心跳时间 + // 2.2 情况二:如果存在,则更新 heartbeatAt + // TODO @haohao:这里最好 new 去 update;避免并发更新(虽然目前没有) pluginInstance.setHeartbeatAt(System.currentTimeMillis()); pluginInstanceMapper.updateById(pluginInstance); } diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java index 8682a549c0..4615dcf96f 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.concurrent.CompletableFuture; +// TODO 芋艿:后续 review 下 /** * 插件实例 RPC 接口 * From deab8c1cc6bb7864d9c40e0c369f649f6f9bfa41 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 9 Jan 2025 12:36:30 +0800 Subject: [PATCH 081/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E6=97=A5?= =?UTF-8?q?=E5=BF=97=20TDengine=20=E8=A1=A8=E4=B8=8E=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E8=AE=BE=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceDataController.java | 7 +++--- .../device/vo/device/IotDeviceSaveReqVO.java | 2 +- .../IotDeviceDataSimulatorSaveReqVO.java | 13 +++++----- .../vo/deviceData/IotDeviceLogPageReqVO.java | 5 +++- .../vo/deviceData/IotDeviceLogRespVO.java | 5 +++- .../thingmodel/IotThingModelController.java | 1 + .../thingmodel/vo/IotThingModelListReqVO.java | 8 +++---- .../dal/dataobject/device/IotDeviceLogDO.java | 24 ++++++++++++------- .../tdengine/ThingModelMessageDO.java | 4 ++-- .../dal/tdengine/IotDeviceLogDataMapper.java | 12 ++++++---- .../tdengine/TdThingModelMessageMapper.java | 6 +---- .../TDengineTableInitConfiguration.java | 9 ++++--- .../device/IotDeviceLogDataService.java | 3 +-- .../device/IotDeviceLogDataServiceImpl.java | 24 +++++++------------ .../IotDevicePropertyDataServiceImpl.java | 2 -- .../product/IotProductServiceImpl.java | 14 +++++------ .../IotThingModelMessageServiceImpl.java | 16 +++++-------- .../mapper/device/IotDeviceLogDataMapper.xml | 22 ++++++++--------- 18 files changed, 90 insertions(+), 87 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 801cbcb214..be451f17a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; @@ -13,7 +12,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -34,7 +32,7 @@ public class IotDeviceDataController { @Resource private IotDeviceLogDataService iotDeviceLogDataService; - @Resource + @Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性 private IotDeviceLogDataService deviceLogDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @@ -52,14 +50,17 @@ public class IotDeviceDataController { PageResult> list = deviceDataService.getHistoryDeviceProperties(deviceDataReqVO); return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); } + // TODO:数据权限 @PostMapping("/simulator") @Operation(summary = "模拟设备") public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { //TODO:先生成一下设备日志 后续完善模拟设备代码逻辑 + // TODO @super:应该 deviceDataService 里面有个 simulatorDevice,然后里面去 insert 日志! iotDeviceLogDataService.createDeviceLog(simulatorReqVO); return success(true); } + // TODO:数据权限 @GetMapping("/log/page") @Operation(summary = "获得设备日志分页") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index e1268b0035..7d9ac6a0d0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -14,7 +14,7 @@ public class IotDeviceSaveReqVO { private Long id; @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.AUTO, example = "177") - @Size(max = 50, message = "设备编号长度不能超过50个字符") + @Size(max = 50, message = "设备编号长度不能超过 50 个字符") private String deviceKey; @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java index 3a4bffacee..c4f9d1f55d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java @@ -3,27 +3,27 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +// TODO super: SaveReqVO => ReqVO @Schema(description = "管理后台 - IoT 模拟设备数据 Request VO") @Data public class IotDeviceDataSimulatorSaveReqVO { - @Schema(description = "消息ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "msg123") + // TODO @super:感觉后端随机更合适? + @Schema(description = "消息 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "msg123") private String id; + // TODO @super:不用传递 productKey,因为 deviceKey 可以推导出来 @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123") @NotEmpty(message = "产品ID不能为空") private String productKey; + // TODO @super:中文写作规范,中英文之间,要有空格。例如说,设备 ID。ps:这里应该是设备标识 @Schema(description = "设备ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") @NotEmpty(message = "设备ID不能为空") private String deviceKey; + // TODO @super:type、subType,是不是不用传递,因为模拟只有属性??? @Schema(description = "消息/日志类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") @NotEmpty(message = "消息类型不能为空") private String type; @@ -36,6 +36,7 @@ public class IotDeviceDataSimulatorSaveReqVO { @NotEmpty(message = "数据内容不能为空") private String content; + // TODO @芋艿:需要讨论下,reportTime 到底以那个为准! @Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED) private Long reportTime; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java index 67099f3318..a882a6d86a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java @@ -18,13 +18,16 @@ public class IotDeviceLogPageReqVO extends PageParam { @NotEmpty(message = "设备标识不能为空") private String deviceKey; + // TODO @super:对应的枚举类 @Schema(description = "消息类型", example = "property") private String type; @Schema(description = "标识符", example = "temperature") + // TODO @super:对应的枚举类 private String subType; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; -} \ No newline at end of file + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogRespVO.java index 1201f7b742..48ea9b6989 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogRespVO.java @@ -11,8 +11,10 @@ public class IotDeviceLogRespVO { @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123") private String productKey; + @Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") private String deviceKey; @@ -30,4 +32,5 @@ public class IotDeviceLogRespVO { @Schema(description = "记录时间戳", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime ts; -} \ No newline at end of file + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java index d213329e29..e4913486d3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java @@ -74,6 +74,7 @@ public class IotThingModelController { return success(IotThingModelConvert.INSTANCE.convertList(list)); } + // TODO @puhui @super:getThingModelListByProductId 和 getThingModelListByProductId 可以融合么? @GetMapping("/list") @Operation(summary = "获得产品物模型列表") @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java index 3652b36b94..dd77cfc5ee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java @@ -6,11 +6,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; - - @Schema(description = "管理后台 - IoT 产品物模型List Request VO") @Data public class IotThingModelListReqVO { + @Schema(description = "功能标识") private String identifier; @@ -21,7 +20,8 @@ public class IotThingModelListReqVO { @InEnum(IotThingModelTypeEnum.class) private Integer type; - @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "产品ID不能为空") + @Schema(description = "产品 ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "产品 ID 不能为空") private Long productId; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java index c82b231c5c..dd811ee323 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java @@ -1,16 +1,15 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; -import cn.hutool.core.date.DateTime; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.time.LocalDateTime; - /** * IoT 设备日志数据 DO * + * 目前使用 TDengine 存储 + * * @author alwayssuper */ @Data @@ -18,33 +17,41 @@ import java.time.LocalDateTime; @NoArgsConstructor @AllArgsConstructor public class IotDeviceLogDO { + + // TODO @芋艿:消息 ID 的生成逻辑 /** - * 消息ID + * 消息 ID */ private String id; + // TODO @super:关联要 @下 /** - * 产品ID + * 产品标识 */ private String productKey; + // TODO @super:关联要 @下 /** - * 设备ID + * 设备标识 */ private String deviceKey; + // TODO @super:枚举类 /** - * 消息/日志类型 + * 日志类型 */ private String type; + // TODO @super:枚举类 /** * 标识符:用于标识具体的属性、事件或服务 */ private String subType; /** - * 数据内容:存储具体的消息数据内容,通常是JSON格式 + * 数据内容 + * + * 存储具体的消息数据内容,通常是 JSON 格式 */ private String content; @@ -58,5 +65,4 @@ public class IotDeviceLogDO { */ private Long ts; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java index b647c68730..ae70da37bb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; // TODO @芋艿:纠结下字段 -@Deprecated +@Deprecated // TODO @super:看看啥时候删除下哈。 /** * TD 物模型消息日志的数据库 */ @@ -25,7 +25,7 @@ public class ThingModelMessageDO { /** * 系统扩展参数 - * + * * 例如:设备状态、系统时间、固件版本等系统级信息 */ private Object system; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index 51fd625ad1..7d3a06633b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; @@ -12,7 +11,7 @@ import java.util.List; /** * IOT 设备日志数据 Mapper 接口 - * + * * 基于 TDengine 实现设备日志的存储 */ @Mapper @@ -22,11 +21,12 @@ public interface IotDeviceLogDataMapper { /** * 创建设备日志超级表 - * + * * 注意:初始化时只需创建一次 */ void createDeviceLogSTable(); + // TODO @super:是不是删除哈 /** * 创建设备日志子表 * @@ -34,11 +34,12 @@ public interface IotDeviceLogDataMapper { */ void createDeviceLogTable(@Param("deviceKey") String deviceKey); + // TODO @super:单个参数,不用加 @Param /** * 插入设备日志数据 - * + * * 如果子表不存在,会自动创建子表 - * + * * @param log 设备日志数据 */ void insert(@Param("log") IotDeviceLogDO log); @@ -58,4 +59,5 @@ public interface IotDeviceLogDataMapper { * @return 日志总数 */ Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java index b04be11991..cbc6e88366 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java @@ -1,10 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessageDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; -import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -13,7 +9,7 @@ import org.apache.ibatis.annotations.Param; * 处理 TD 中物模型消息日志的操作 */ @Mapper -@Deprecated +@Deprecated // TODO super:什么时候,删除下哈。 @TDengineDS @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 public interface TdThingModelMessageMapper { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java index e14fa2948c..e0057e1808 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java @@ -16,7 +16,7 @@ import org.springframework.core.annotation.Order; @Slf4j @RequiredArgsConstructor @Configuration -@Order(Integer.MAX_VALUE) // 保证在最后执行 +@Order public class TDengineTableInitConfiguration implements ApplicationRunner { private final IotDeviceLogDataService deviceLogService; @@ -26,15 +26,18 @@ public class TDengineTableInitConfiguration implements ApplicationRunner { try { // 初始化设备日志表 deviceLogService.initTDengineSTable(); - log.info("初始化 设备日志表 TDengine 表结构成功"); + // TODO @super:这个日志,是不是不用打,不然重复啦!!! + log.info("[run]初始化 设备日志表 TDengine 表结构成功"); } catch (Exception ex) { + // TODO @super:初始化失败,打印 error 日志,退出系统。。不然跑起来,就初始啦!!! if (ex.getMessage().contains("Table already exists")) { log.info("TDengine 设备日志超级表已存在,跳过创建"); return; - }else{ + } else{ log.error("初始化 设备日志表 TDengine 表结构失败", ex); } throw ex; } } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java index 72cbf63e9c..1a3b48503c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java @@ -5,7 +5,6 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; - /** * IoT 设备日志数据 Service 接口 * @@ -15,7 +14,7 @@ public interface IotDeviceLogDataService { /** * 初始化 TDengine 超级表 - * + * *系统启动时,会自动初始化一次 */ void initTDengineSTable(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index bf883526d7..d0659e5073 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.service.device; -import cn.hutool.core.date.DateTime; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; @@ -12,8 +11,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.List; /** @@ -29,34 +26,30 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ @Resource private IotDeviceLogDataMapper iotDeviceLogDataMapper; - + // TODO @super:方法名。defineDeviceLog。。未来,有可能别人使用别的记录日志,例如说 es 之类的。 @Override public void initTDengineSTable() { - try { - // 创建设备日志超级表 - iotDeviceLogDataMapper.createDeviceLogSTable(); - log.info("创建设备日志超级表成功"); - } catch (Exception ex) { - throw ex; - } + // TODO @super:改成不存在才创建。 + iotDeviceLogDataMapper.createDeviceLogSTable(); } @Override public void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { - //TODO:讨论一下,iotkit这块TS和上报时间都是外部传入的 但是看TDengine文档 他是建议对TS在SQL中直接NOW 咱们的TS数据获取是走哪一种 - // 1. 转换请求对象为 DO IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class); - + // 2. 处理时间字段 + // TODO @super:一次性的字段,不用单独给个变量 long currentTime = System.currentTimeMillis(); // 2.1 设置时序时间为当前时间 - iotDeviceLogDO.setTs(currentTime); + iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now() // 3. 插入数据 + // TODO @super:不要直接调用对方的 IotDeviceLogDataMapper,通过 service 哈! iotDeviceLogDataMapper.insert(iotDeviceLogDO); } + // TODO @super:在 iotDeviceLogDataService 写 @Override public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { // 查询数据 @@ -65,4 +58,5 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ // 构造分页结果 return new PageResult<>(list, total); } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 5ca4cffe70..0f9523414e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -18,7 +18,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; @@ -111,7 +110,6 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe return; } newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); - // 2.1.1 创建产品超级表 devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); return; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 41537664b0..ad3ff94e2b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -116,14 +115,15 @@ public class IotProductServiceImpl implements IotProductService { public void updateProductStatus(Long id, Integer status) { // 1. 校验存在 validateProductExists(id); - // 2. 更新 - IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); - // 3. 产品是发布状态 - if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { - // 3.1 创建产品超级表数据模型 - devicePropertyDataService.defineDevicePropertyData(id); + // 2. 产品是发布状态 + if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { + // 创建产品超级表数据模型 + devicePropertyDataService.defineDevicePropertyData(id); } + + // 3. 更新 + IotProductDO updateObj = IotProductDO.builder().id(id).status(status).build(); productMapper.updateById(updateObj); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index 52e90f30f7..e094b34cb6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -7,20 +7,20 @@ import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdThingModelMessageMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.util.IotTdDatabaseUtils; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -61,13 +61,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private TdEngineDMLMapper tdEngineDMLMapper; - @Resource - private TdThingModelMessageMapper tdThingModelMessageMapper; - @Resource private DeviceDataRedisDAO deviceDataRedisDAO; - - + // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 @Override @TenantIgnore diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index dd0f80a949..09e5dd4681 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -7,19 +7,19 @@ CREATE STABLE device_log ( - ts TIMESTAMP, - id NCHAR(50), - product_key NCHAR(50), - type NCHAR(50), - subType NCHAR(50), - content NCHAR(1024), - report_time TIMESTAMP - )TAGS ( - device_key NCHAR(50) - ) + ts TIMESTAMP, + id NCHAR(50), + product_key NCHAR(50), + type NCHAR(50), + + subType NCHAR(50), + content NCHAR(1024), + report_time TIMESTAMP + ) TAGS ( + device_key NCHAR(50) + ) - CREATE TABLE device_log_${deviceKey} USING device_log TAGS('${deviceKey}') From d9dda54ccedb9dbf9ea13dc1190f95a136f55db5 Mon Sep 17 00:00:00 2001 From: alwayssuper <12851801+alwayssuper@user.noreply.gitee.com> Date: Fri, 10 Jan 2025 20:42:00 +0800 Subject: [PATCH 082/309] local --- .../dal/tdengine/IotDeviceLogDataMapper.java | 39 +++++++++++++++++++ .../device/IotDeviceLogDataService.java | 22 +++++++++++ .../src/main/resources/application-local.yaml | 31 ++++++++++----- .../src/main/resources/application.yaml | 5 ++- 4 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java new file mode 100644 index 0000000000..194218b7f8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.dal.tdengine; + +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * IoT 设备日志 Mapper + * + * @author alwayssuper + */ +@Mapper +@TDengineDS +@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 +public interface IotDeviceLogDataMapper { + + /** + * 创建设备日志超级表 + * 初始化只创建一次 + */ + void createDeviceLogSTable(); + + /** + * 创建设备日志子表 + * + * @param deviceKey 设备标识 + */ + void createDeviceLogTable( @Param("deviceKey") String deviceKey); + + /** + * 插入设备日志数据 + * + * @param log 设备日志数据 + */ + void insert(@Param("log") IotDeviceLogDO log); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java new file mode 100644 index 0000000000..166727dec7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.service.device; + +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; + +/** + * IoT 设备日志数据 Service 接口 + * + * @author alwayssuper + */ +public interface IotDeviceLogDataService { + + /** + * 初始化 TDengine 表 + */ + void initTDengineSTable(); + + /** + * 模拟设备创建设备日志 + * @param simulatorReqVO 模拟设备信息 + */ + void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO); +} diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index e5ae6d195a..fec1ad3506 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,7 +45,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 @@ -53,8 +53,8 @@ spring: # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: root - password: ahh@123456 + username: ruoyi-vue-pro + password: ruoyi-@h2ju02hebp # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -63,17 +63,25 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: ruoyi-vue-pro + password: ruoyi-@h2ju02hebp + tdengine: # IOT 数据库 +# lazy: true # 开启懒加载,保证启动速度 + url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro + driver-class-name: com.taosdata.jdbc.rs.RestfulDriver username: root - password: ahh@123456 + password: taosdata + druid: + validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: 127.0.0.1 # 地址 + host: chaojiniu.top # 地址 port: 6379 # 端口 - database: 0 # 数据库索引 -# password: dev # 密码,建议生产环境开启 + database: 15 # 数据库索引 + password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### @@ -175,8 +183,10 @@ logging: cn.iocoder.yudao.module.crm.dal.mysql: debug cn.iocoder.yudao.module.erp.dal.mysql: debug cn.iocoder.yudao.module.iot.dal.mysql: debug + cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG cn.iocoder.yudao.module.ai.dal.mysql: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + com.taosdata: DEBUG # TDengine 的日志级别 debug: false @@ -259,7 +269,7 @@ justauth: iot: emq: # 账号 - username: anhaohao + username: haohao # 密码 password: ahh@123456 # 主机地址 @@ -271,4 +281,5 @@ iot: # 保持连接 keepalive: 60 # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) - clearSession: true \ No newline at end of file + clearSession: true + diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index baf68657e0..933e024356 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -328,4 +328,7 @@ yudao: key: pLXUGAwK5305 customer: E77DF18BE109F454A5CD319E44BF5177 -debug: false \ No newline at end of file +debug: false +# 插件配置 +pf4j: + pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录 \ No newline at end of file From 62a868f497874f5d755ec1bff4b74a08c8bb05a5 Mon Sep 17 00:00:00 2001 From: alwayssuper <12851801+alwayssuper@user.noreply.gitee.com> Date: Sun, 12 Jan 2025 20:13:41 +0800 Subject: [PATCH 083/309] feat:simulator1 --- .../dal/tdengine/IotDeviceLogDataMapper.java | 34 +++++-------------- .../tdengine/TdThingModelMessageMapper.java | 31 ----------------- .../TDengineTableInitConfiguration.java | 13 +++---- .../device/IotDeviceLogDataService.java | 2 +- .../device/IotDeviceLogDataServiceImpl.java | 19 +++++++++-- .../mapper/device/IotDeviceLogDataMapper.xml | 9 +++-- .../src/main/resources/application-local.yaml | 6 ++-- 7 files changed, 40 insertions(+), 74 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index 9584262dff..42dcc0cf38 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -21,32 +21,10 @@ public interface IotDeviceLogDataMapper { /** * 创建设备日志超级表 -<<<<<<< HEAD * 初始化只创建一次 */ void createDeviceLogSTable(); -======= - * - * 注意:初始化时只需创建一次 - */ - void createDeviceLogSTable(); - - // TODO @super:是不是删除哈 ->>>>>>> deab8c1cc6bb7864d9c40e0c369f649f6f9bfa41 - /** - * 创建设备日志子表 - * - * @param deviceKey 设备标识 - */ -<<<<<<< HEAD - void createDeviceLogTable( @Param("deviceKey") String deviceKey); - - /** - * 插入设备日志数据 - * -======= - void createDeviceLogTable(@Param("deviceKey") String deviceKey); // TODO @super:单个参数,不用加 @Param /** @@ -54,10 +32,9 @@ public interface IotDeviceLogDataMapper { * * 如果子表不存在,会自动创建子表 * ->>>>>>> deab8c1cc6bb7864d9c40e0c369f649f6f9bfa41 * @param log 设备日志数据 */ - void insert(@Param("log") IotDeviceLogDO log); + void insert(IotDeviceLogDO log); /** * 获得设备日志分页 @@ -65,7 +42,7 @@ public interface IotDeviceLogDataMapper { * @param reqVO 分页查询条件 * @return 设备日志列表 */ - List selectPage(@Param("reqVO") IotDeviceLogPageReqVO reqVO); + List selectPage(IotDeviceLogPageReqVO reqVO); /** * 获得设备日志总数 @@ -73,6 +50,11 @@ public interface IotDeviceLogDataMapper { * @param reqVO 查询条件 * @return 日志总数 */ - Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO); + Long selectCount(IotDeviceLogPageReqVO reqVO); + /** + * 查询设备日志表是否存在 + * + */ + Object checkDeviceLogTableExists(); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java deleted file mode 100644 index cbc6e88366..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdThingModelMessageMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; -import com.baomidou.mybatisplus.annotation.InterceptorIgnore; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -/** - * 处理 TD 中物模型消息日志的操作 - */ -@Mapper -@Deprecated // TODO super:什么时候,删除下哈。 -@TDengineDS -@InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 -public interface TdThingModelMessageMapper { - - /** - * 创建物模型消息日志超级表超级表 - * - */ - - void createSuperTable(@Param("productKey") String productKey); - - /** - * 创建子表 - * - */ - - void createTableWithTag(@Param("productKey") String productKey,@Param("deviceKey") String deviceKey); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java index e0057e1808..c9f08903e8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java @@ -25,18 +25,13 @@ public class TDengineTableInitConfiguration implements ApplicationRunner { public void run(ApplicationArguments args) { try { // 初始化设备日志表 - deviceLogService.initTDengineSTable(); + deviceLogService.defineDeviceLog(); // TODO @super:这个日志,是不是不用打,不然重复啦!!! - log.info("[run]初始化 设备日志表 TDengine 表结构成功"); } catch (Exception ex) { // TODO @super:初始化失败,打印 error 日志,退出系统。。不然跑起来,就初始啦!!! - if (ex.getMessage().contains("Table already exists")) { - log.info("TDengine 设备日志超级表已存在,跳过创建"); - return; - } else{ - log.error("初始化 设备日志表 TDengine 表结构失败", ex); - } - throw ex; + // 初始化失败时打印错误日志并退出系统 + log.error("[TDengine] 初始化设备日志表结构失败,系统无法正常运行,即将退出", ex); + System.exit(1); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java index 1a3b48503c..a92d46bf49 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java @@ -17,7 +17,7 @@ public interface IotDeviceLogDataService { * *系统启动时,会自动初始化一次 */ - void initTDengineSTable(); + void defineDeviceLog(); /** * 插入设备日志 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index d0659e5073..769821e3e4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -28,9 +28,24 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ // TODO @super:方法名。defineDeviceLog。。未来,有可能别人使用别的记录日志,例如说 es 之类的。 @Override - public void initTDengineSTable() { + public void defineDeviceLog() { // TODO @super:改成不存在才创建。 - iotDeviceLogDataMapper.createDeviceLogSTable(); +// try { +// // 创建超级表(使用 IF NOT EXISTS 语句避免重复创建错误) +// iotDeviceLogDataMapper.createDeviceLogSTable(); +// } catch (Exception e) { +// if (e.getMessage().contains("already exists")) { +// log.info("[TDengine] 设备日志超级表已存在,跳过创建"); +// return; +// } +// throw e; +// } + if(iotDeviceLogDataMapper.checkDeviceLogTableExists()==null){ + log.info("[TDengine] 设备日志超级表不存在,开始创建 {}",iotDeviceLogDataMapper.checkDeviceLogTableExists()); + iotDeviceLogDataMapper.createDeviceLogSTable(); + }else{ + log.info("[TDengine] 设备日志超级表已存在,跳过创建"); + } } @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 09e5dd4681..1554b7d1e0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -6,13 +6,13 @@ - CREATE STABLE device_log ( + CREATE STABLE IF NOT EXISTS device_log ( ts TIMESTAMP, id NCHAR(50), product_key NCHAR(50), type NCHAR(50), - subType NCHAR(50), + sub_type NCHAR(50), content NCHAR(1024), report_time TIMESTAMP ) TAGS ( @@ -75,4 +75,9 @@ + + + \ No newline at end of file diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 518aa69a4c..08918a667b 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -283,9 +283,9 @@ iot: # MQTT-RPC 配置 mqtt: - broker: tcp://127.0.0.1:1883 - username: root - password: 123456 + broker: tcp://chaojiniu.top:1883 + username: haohao + password: ahh@123456 clientId: mqtt-rpc-server-${random.int} requestTopic: rpc/request responseTopicPrefix: rpc/response/ From 890d304340f1d20c52cf5288d47956eaed5814ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 15 Jan 2025 22:37:07 +0800 Subject: [PATCH 084/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=9B=B4=E6=96=B0=20Vert.x=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E8=87=B3=204.5.1=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20EMQX=20=E6=8F=92=E4=BB=B6=E5=8F=8A=E5=85=B6=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E9=85=8D=E7=BD=AE=EF=BC=8C=E9=87=8D=E6=9E=84=20MQTT?= =?UTF-8?q?=20=E6=8F=92=E4=BB=B6=E4=BB=A5=E6=94=AF=E6=8C=81=20Vert.x=20MQT?= =?UTF-8?q?T=20=E6=9C=8D=E5=8A=A1=E5=99=A8=EF=BC=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=90=AF=E5=8A=A8=E5=92=8C=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=BF=A1=E6=81=AF=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 6 + .../plugin.properties | 6 + .../yudao-module-iot-emqx-plugin/pom.xml | 164 +++++++++++++ .../src/main/assembly/assembly.xml | 31 +++ .../yudao/module/iot/plugin/EmqxPlugin.java | 45 ++++ .../yudao-module-iot-http-plugin/pom.xml | 13 +- .../plugin.properties | 7 +- .../yudao-module-iot-mqtt-plugin/pom.xml | 7 +- .../yudao/module/iot/plugin/MqttPlugin.java | 39 ++- .../iot/plugin/MqttServerExtension.java | 231 ++++++++++++++++++ 10 files changed, 508 insertions(+), 41 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/assembly/assembly.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index fb3cf8562d..0a9d0bf454 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -626,6 +626,12 @@ vertx-web ${vertx.version} + + + io.vertx + vertx-mqtt + ${vertx.version} + diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties new file mode 100644 index 0000000000..a23bafcf79 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties @@ -0,0 +1,6 @@ +plugin.id=emqx-plugin +plugin.class=cn.iocoder.yudao.module.iot.plugin.EmqxPlugin +plugin.version=0.0.1 +plugin.provider=ahh +plugin.dependencies= +plugin.description=emqx-plugin-0.0.1 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml new file mode 100644 index 0000000000..43d67f5207 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml @@ -0,0 +1,164 @@ + + + + yudao-module-iot-plugin + cn.iocoder.boot + ${revision} + + 4.0.0 + jar + + yudao-module-iot-emqx-plugin + + ${project.artifactId} + + 物联网 插件模块 - emqx 插件 + + + + + emqx-plugin + cn.iocoder.yudao.module.iot.plugin.EmqxPlugin + 0.0.1 + ahh + emqx-plugin-0.0.1 + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + unzip jar file + package + + + + + + + run + + + + + + + maven-assembly-plugin + 2.3 + + + + src/main/assembly/assembly.xml + + + false + + + + make-assembly + package + + attached + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + ${plugin.id} + ${plugin.class} + ${plugin.version} + ${plugin.provider} + ${plugin.description} + ${plugin.dependencies} + + + + + + + maven-deploy-plugin + + true + + + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.pf4j + pf4j-spring + provided + + + + cn.iocoder.boot + yudao-module-iot-api + ${revision} + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + io.vertx + vertx-core + + + + io.vertx + vertx-web + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..daec9e4315 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/assembly/assembly.xml @@ -0,0 +1,31 @@ + + plugin + + zip + + false + + + false + runtime + lib + + *:jar:* + + + + + + + target/plugin-classes + classes + + + diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java new file mode 100644 index 0000000000..e64695b06d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; + +import javax.annotation.Resource; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +public class EmqxPlugin extends Plugin { + + private ExecutorService executorService; + @Resource + private DeviceDataApi deviceDataApi; + + public EmqxPlugin(PluginWrapper wrapper) { + super(wrapper); + this.executorService = Executors.newSingleThreadExecutor(); + } + + @Override + public void start() { + log.info("EmqxPlugin.start()"); + + if (executorService.isShutdown() || executorService.isTerminated()) { + executorService = Executors.newSingleThreadExecutor(); + } + + deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); + if (deviceDataApi == null) { + log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); + return; + } + + } + + @Override + public void stop() { + log.info("EmqxPlugin.stop()"); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index 29c0200f1c..22cb439681 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -147,20 +147,11 @@ ${lombok.version} provided - - - io.vertx - vertx-core - - + io.vertx vertx-web - - - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 + 4.5.11 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/plugin.properties index 31050c5bac..939e0f6929 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/plugin.properties +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/plugin.properties @@ -1,6 +1,7 @@ plugin.id=mqtt-plugin +plugin.description=Vert.x MQTT plugin plugin.class=cn.iocoder.yudao.module.iot.plugin.MqttPlugin -plugin.version=0.0.1 +plugin.version=1.0.0 +plugin.requires= plugin.provider=ahh -plugin.dependencies= -plugin.description=mqtt-plugin-0.0.1 +plugin.license=Apache-2.0 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml index 9607e0f93c..462fbd0901 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml @@ -145,10 +145,11 @@ ${lombok.version} provided - + - org.eclipse.paho - org.eclipse.paho.client.mqttv3 + io.vertx + vertx-mqtt + 4.5.11 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java index b3749e4025..54ff31f36b 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java @@ -1,45 +1,36 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; import org.pf4j.PluginWrapper; -import javax.annotation.Resource; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - @Slf4j public class MqttPlugin extends Plugin { - private ExecutorService executorService; - @Resource - private DeviceDataApi deviceDataApi; + private MqttServerExtension mqttServerExtension; public MqttPlugin(PluginWrapper wrapper) { super(wrapper); - this.executorService = Executors.newSingleThreadExecutor(); } @Override public void start() { - log.info("MqttPlugin.start()"); - - if (executorService.isShutdown() || executorService.isTerminated()) { - executorService = Executors.newSingleThreadExecutor(); - } - - deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); - if (deviceDataApi == null) { - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); - return; - } - + log.info("MQTT Plugin started."); + mqttServerExtension = new MqttServerExtension(); + mqttServerExtension.startMqttServer(); } @Override public void stop() { - log.info("MqttPlugin.stop()"); + log.info("MQTT Plugin stopped."); + if (mqttServerExtension != null) { + mqttServerExtension.stopMqttServer().onComplete(ar -> { + if (ar.succeeded()) { + log.info("Stopped MQTT Server successfully"); + } else { + log.error("Failed to stop MQTT Server: {}", ar.cause().getMessage()); + } + }); + } } -} \ No newline at end of file +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java new file mode 100644 index 0000000000..868d238ee9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java @@ -0,0 +1,231 @@ +package cn.iocoder.yudao.module.iot.plugin; + +import io.netty.handler.codec.mqtt.MqttProperties; +import io.netty.handler.codec.mqtt.MqttQoS; +import io.vertx.core.Future; +import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; +import io.vertx.mqtt.MqttEndpoint; +import io.vertx.mqtt.MqttServer; +import io.vertx.mqtt.MqttServerOptions; +import io.vertx.mqtt.MqttTopicSubscription; +import io.vertx.mqtt.messages.MqttDisconnectMessage; +import io.vertx.mqtt.messages.MqttPublishMessage; +import io.vertx.mqtt.messages.MqttSubscribeMessage; +import io.vertx.mqtt.messages.MqttUnsubscribeMessage; +import io.vertx.mqtt.messages.codes.MqttSubAckReasonCode; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.Extension; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * 根据官方示例,整合常见 MQTT 功能到 PF4J 的 Extension 类中 + */ +@Slf4j +@Extension +public class MqttServerExtension { + + private Vertx vertx; + private MqttServer mqttServer; + + /** + * 启动 MQTT 服务端 + * 可根据需要决定是否启用 SSL/TLS、WebSocket、多实例部署等 + */ + public void startMqttServer() { + // 初始化 Vert.x + vertx = Vertx.vertx(); + + // ========== 如果需要 SSL/TLS,请参考下面注释,启用注释并替换端口、证书路径等 ========== + // MqttServerOptions options = new MqttServerOptions() + // .setPort(8883) + // .setKeyCertOptions(new PemKeyCertOptions() + // .setKeyPath("./src/test/resources/tls/server-key.pem") + // .setCertPath("./src/test/resources/tls/server-cert.pem")) + // .setSsl(true); + + // ========== 如果需要 WebSocket,请设置 setUseWebSocket(true) ========== + // options.setUseWebSocket(true); + + // ========== 默认不启用 SSL 的示例 ========== + MqttServerOptions options = new MqttServerOptions() + .setPort(1883) + .setHost("0.0.0.0") + .setUseWebSocket(false); // 如果需要 WebSocket,请改为 true + + mqttServer = MqttServer.create(vertx, options); + + // 指定 endpointHandler,处理客户端连接等 + mqttServer.endpointHandler(endpoint -> { + handleClientConnect(endpoint); + handleDisconnect(endpoint); + handleSubscribe(endpoint); + handleUnsubscribe(endpoint); + handlePublish(endpoint); + handlePing(endpoint); + }); + + // 启动监听 + mqttServer.listen(ar -> { + if (ar.succeeded()) { + log.info("MQTT server is listening on port {}", mqttServer.actualPort()); + } else { + log.error("Error on starting the server", ar.cause()); + } + }); + } + + /** + * 优雅关闭 MQTT 服务端 + */ + public Future stopMqttServer() { + if (mqttServer != null) { + return mqttServer.close().onComplete(ar -> { + if (ar.succeeded()) { + log.info("MQTT server closed."); + if (vertx != null) { + vertx.close(); + log.info("Vert.x instance closed."); + } + } else { + log.error("Failed to close MQTT server: {}", ar.cause().getMessage()); + } + }); + } + return Future.succeededFuture(); + } + + // ==================== 以下为官方示例中常见事件的处理封装 ==================== + + /** + * 处理客户端连接 (CONNECT) + */ + private void handleClientConnect(MqttEndpoint endpoint) { + // 打印 CONNECT 的主要信息 + log.info("MQTT client [{}] request to connect, clean session = {}", + endpoint.clientIdentifier(), endpoint.isCleanSession()); + + if (endpoint.auth() != null) { + log.info("[username = {}, password = {}]", endpoint.auth().getUsername(), endpoint.auth().getPassword()); + } + log.info("[properties = {}]", endpoint.connectProperties()); + + if (endpoint.will() != null) { + log.info("[will topic = {}, msg = {}, QoS = {}, isRetain = {}]", + endpoint.will().getWillTopic(), + new String(endpoint.will().getWillMessageBytes()), + endpoint.will().getWillQos(), + endpoint.will().isWillRetain()); + } + + log.info("[keep alive timeout = {}]", endpoint.keepAliveTimeSeconds()); + + // 接受远程客户端的连接 + endpoint.accept(false); + } + + /** + * 处理客户端主动断开 (DISCONNECT) + */ + private void handleDisconnect(MqttEndpoint endpoint) { + endpoint.disconnectMessageHandler((MqttDisconnectMessage disconnectMessage) -> { + log.info("Received disconnect from client [{}], reason code = {}", + endpoint.clientIdentifier(), disconnectMessage.code()); + }); + } + + /** + * 处理客户端订阅 (SUBSCRIBE) + */ + private void handleSubscribe(MqttEndpoint endpoint) { + endpoint.subscribeHandler((MqttSubscribeMessage subscribe) -> { + List reasonCodes = new ArrayList<>(); + for (MqttTopicSubscription s : subscribe.topicSubscriptions()) { + log.info("Subscription for {} with QoS {}", s.topicName(), s.qualityOfService()); + // 将客户端请求的 QoS 转换为返回给客户端的 reason code(可能是错误码或实际 granted QoS) + reasonCodes.add(MqttSubAckReasonCode.qosGranted(s.qualityOfService())); + } + // 回复 SUBACK,MQTT 5.0 时可指定 reasonCodes、properties + endpoint.subscribeAcknowledge(subscribe.messageId(), reasonCodes, MqttProperties.NO_PROPERTIES); + }); + } + + /** + * 处理客户端取消订阅 (UNSUBSCRIBE) + */ + private void handleUnsubscribe(MqttEndpoint endpoint) { + endpoint.unsubscribeHandler((MqttUnsubscribeMessage unsubscribe) -> { + for (String topic : unsubscribe.topics()) { + log.info("Unsubscription for {}", topic); + } + // 回复 UNSUBACK,MQTT 5.0 时可指定 reasonCodes、properties + endpoint.unsubscribeAcknowledge(unsubscribe.messageId()); + }); + } + + /** + * 处理客户端发布的消息 (PUBLISH) + */ + private void handlePublish(MqttEndpoint endpoint) { + // 接收 PUBLISH 消息 + endpoint.publishHandler((MqttPublishMessage message) -> { + String payload = message.payload().toString(Charset.defaultCharset()); + log.info("Received message [{}] on topic [{}] with QoS [{}]", + payload, message.topicName(), message.qosLevel()); + + // 根据不同 QoS,回复客户端 + if (message.qosLevel() == MqttQoS.AT_LEAST_ONCE) { + endpoint.publishAcknowledge(message.messageId()); + } else if (message.qosLevel() == MqttQoS.EXACTLY_ONCE) { + endpoint.publishReceived(message.messageId()); + } + }); + + // 如果 QoS = 2,需要处理 PUBREL + endpoint.publishReleaseHandler(messageId -> { + endpoint.publishComplete(messageId); + }); + } + + /** + * 处理客户端 PINGREQ + */ + private void handlePing(MqttEndpoint endpoint) { + endpoint.pingHandler(v -> { + // 这里仅做日志, PINGRESP 已自动发送 + log.info("Ping received from client [{}]", endpoint.clientIdentifier()); + }); + } + + // ==================== 如果需要服务端向客户端发布消息,可用以下示例 ==================== + + /** + * 服务端主动向已连接的某个 endpoint 发布消息的示例 + * 如果使用 MQTT 5.0,可以传递更多消息属性 + */ + public void publishToClient(MqttEndpoint endpoint, String topic, String content) { + endpoint.publish(topic, + Buffer.buffer(content), + MqttQoS.AT_LEAST_ONCE, // QoS 自行选择 + false, + false); + + // 处理 QoS 1 和 QoS 2 的 ACK + endpoint.publishAcknowledgeHandler(messageId -> { + log.info("Received PUBACK from client [{}] for messageId = {}", endpoint.clientIdentifier(), messageId); + }).publishReceivedHandler(messageId -> { + endpoint.publishRelease(messageId); + }).publishCompletionHandler(messageId -> { + log.info("Received PUBCOMP from client [{}] for messageId = {}", endpoint.clientIdentifier(), messageId); + }); + } + + // ==================== 如果需要多实例部署,用于多核扩展,可参考以下思路 ==================== + // 例如,在宿主应用或插件中循环启动多个 MqttServerExtension 实例,或使用 Vert.x 的 deployVerticle: + // DeploymentOptions options = new DeploymentOptions().setInstances(10); + // vertx.deployVerticle(() -> new MyMqttVerticle(), options); + +} From 552432455415142c5b57f403b52bba7eacc008e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 19 Jan 2025 09:53:57 +0800 Subject: [PATCH 085/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=20MQTT=20RPC=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=92=8C=E5=AE=9E=E7=8E=B0=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E6=8B=AC=20MqttConfig=E3=80=81RpcServer=E3=80=81RpcClient=20?= =?UTF-8?q?=E5=92=8C=20RpcController=20=E7=B1=BB=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=E4=BB=A5=E6=B8=85?= =?UTF-8?q?=E7=90=86=E6=9C=AA=E4=BD=BF=E7=94=A8=E7=9A=84=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/mqttrpc/config/MqttConfig.java | 40 ------- .../module/iot/mqttrpc/server/RpcServer.java | 100 ------------------ .../module/iot/controller/RpcController.java | 38 ------- .../module/iot/mqttrpc/client/RpcClient.java | 93 ---------------- .../module/iot/mqttrpc/config/MqttConfig.java | 41 ------- 5 files changed, 312 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java deleted file mode 100644 index c7a0500030..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Data -@Configuration -@ConfigurationProperties(prefix = "mqtt") -public class MqttConfig { - /** - * MQTT 代理地址 - */ - private String broker; - - /** - * MQTT 用户名 - */ - private String username; - - /** - * MQTT 密码 - */ - private String password; - - /** - * MQTT 客户端 ID - */ - private String clientId; - - /** - * MQTT 请求主题 - */ - private String requestTopic; - - /** - * MQTT 响应主题前缀 - */ - private String responseTopicPrefix; -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java deleted file mode 100644 index 90ce2a3875..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/server/RpcServer.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.server; - -import cn.hutool.core.lang.UUID; -import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcRequest; -import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcResponse; -import cn.iocoder.yudao.module.iot.mqttrpc.common.SerializationUtils; -import cn.iocoder.yudao.module.iot.mqttrpc.config.MqttConfig; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.paho.client.mqttv3.*; -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.HashMap; -import java.util.Map; - -// TODO @芋艿:server 逻辑,再瞅瞅; -// TODO @haohao:如果只写在 iot biz 里,那么后续 server => client 貌似不方便?微信再讨论下~; -@Service -@Slf4j -public class RpcServer { - - private final MqttConfig mqttConfig; - private final MqttClient mqttClient; - private final Map methodRegistry = new HashMap<>(); - - public RpcServer(MqttConfig mqttConfig) throws MqttException { - this.mqttConfig = mqttConfig; - this.mqttClient = new MqttClient(mqttConfig.getBroker(), "rpc-server-" + UUID.randomUUID(), new MemoryPersistence()); - MqttConnectOptions options = new MqttConnectOptions(); - options.setAutomaticReconnect(true); - options.setCleanSession(true); - options.setUserName(mqttConfig.getUsername()); - options.setPassword(mqttConfig.getPassword().toCharArray()); - this.mqttClient.connect(options); - } - - @PostConstruct - public void init() throws MqttException { - mqttClient.subscribe(mqttConfig.getRequestTopic(), this::handleRequest); - log.info("RPC Server subscribed to topic: {}", mqttConfig.getRequestTopic()); - } - - private void handleRequest(String topic, MqttMessage message) { - RpcRequest request = SerializationUtils.deserialize(new String(message.getPayload()), RpcRequest.class); - RpcResponse response = new RpcResponse(); - response.setCorrelationId(request.getCorrelationId()); - - try { - MethodInvoker invoker = methodRegistry.get(request.getMethod()); - if (invoker == null) { - throw new NoSuchMethodException("Unknown method: " + request.getMethod()); - } - Object result = invoker.invoke(request.getParams()); - response.setResult(result); - } catch (Exception e) { - response.setError(e.getMessage()); - log.error("Error processing RPC request: {}", e.getMessage(), e); - } - - String replyPayload = SerializationUtils.serialize(response); - MqttMessage replyMessage = new MqttMessage(replyPayload.getBytes()); - replyMessage.setQos(1); - try { - mqttClient.publish(request.getReplyTo(), replyMessage); - log.info("Published response to {}", request.getReplyTo()); - } catch (MqttException e) { - log.error("Failed to publish response: {}", e.getMessage(), e); - } - } - - /** - * 注册可调用的方法 - * - * @param methodName 方法名称 - * @param invoker 方法调用器 - */ - public void registerMethod(String methodName, MethodInvoker invoker) { - methodRegistry.put(methodName, invoker); - log.info("Registered method: {}", methodName); - } - - @PreDestroy - public void cleanup() throws MqttException { - mqttClient.disconnect(); - log.info("RPC Server disconnected"); - } - - /** - * 方法调用器接口 - */ - @FunctionalInterface - public interface MethodInvoker { - - Object invoke(Object[] params) throws Exception; - - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java deleted file mode 100644 index 4615dcf96f..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/controller/RpcController.java +++ /dev/null @@ -1,38 +0,0 @@ - -package cn.iocoder.yudao.module.iot.controller; - -import cn.iocoder.yudao.module.iot.mqttrpc.client.RpcClient; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; -import java.util.concurrent.CompletableFuture; - -// TODO 芋艿:后续 review 下 -/** - * 插件实例 RPC 接口 - * - * @author 芋道源码 - */ -@RestController -@RequestMapping("/rpc") -@RequiredArgsConstructor -public class RpcController { - - @Resource - private RpcClient rpcClient; - - @PostMapping("/add") - public CompletableFuture add(@RequestParam int a, @RequestParam int b) throws Exception { - return rpcClient.call("add", new Object[]{a, b}, 10); - } - - @PostMapping("/concat") - public CompletableFuture concat(@RequestParam String str1, @RequestParam String str2) throws Exception { - return rpcClient.call("concat", new Object[]{str1, str2}, 10); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java deleted file mode 100644 index b73f88c537..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/client/RpcClient.java +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.client; - -import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcRequest; -import cn.iocoder.yudao.module.iot.mqttrpc.common.RpcResponse; -import cn.iocoder.yudao.module.iot.mqttrpc.common.SerializationUtils; -import cn.iocoder.yudao.module.iot.mqttrpc.config.MqttConfig; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.paho.client.mqttv3.MqttClient; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; -import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.UUID; -import java.util.concurrent.*; - -// TODO @芋艿:需要考虑,怎么公用! -@Service -@Slf4j -public class RpcClient { - - private final MqttConfig mqttConfig; - private final MqttClient mqttClient; - private final ConcurrentMap> pendingRequests = new ConcurrentHashMap<>(); - private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - - public RpcClient(MqttConfig mqttConfig) throws MqttException { - this.mqttConfig = mqttConfig; - this.mqttClient = new MqttClient(mqttConfig.getBroker(), mqttConfig.getClientId(), new MemoryPersistence()); - MqttConnectOptions options = new MqttConnectOptions(); - options.setAutomaticReconnect(true); - options.setCleanSession(true); - options.setUserName(mqttConfig.getUsername()); - options.setPassword(mqttConfig.getPassword().toCharArray()); - this.mqttClient.connect(options); - } - - @PostConstruct - public void init() throws MqttException { - mqttClient.subscribe(mqttConfig.getResponseTopicPrefix() + "#", this::handleResponse); - log.info("RPC Client subscribed to topics: {}", mqttConfig.getResponseTopicPrefix() + "#"); - } - - private void handleResponse(String topic, MqttMessage message) { - String correlationId = topic.substring(mqttConfig.getResponseTopicPrefix().length()); - RpcResponse response = SerializationUtils.deserialize(new String(message.getPayload()), RpcResponse.class); - CompletableFuture future = pendingRequests.remove(correlationId); - if (future != null) { - if (response.getError() != null) { - future.completeExceptionally(new RuntimeException(response.getError())); - } else { - future.complete(response); - } - } else { - log.warn("Received response for unknown correlationId: {}", correlationId); - } - } - - public CompletableFuture call(String method, Object[] params, int timeoutSeconds) throws MqttException { - String correlationId = UUID.randomUUID().toString(); - String replyTo = mqttConfig.getResponseTopicPrefix() + correlationId; - - RpcRequest request = new RpcRequest(method, params, correlationId, replyTo); - String payload = SerializationUtils.serialize(request); - MqttMessage message = new MqttMessage(payload.getBytes()); - message.setQos(1); - mqttClient.publish(mqttConfig.getRequestTopic(), message); - - CompletableFuture futureResponse = new CompletableFuture<>(); - pendingRequests.put(correlationId, futureResponse); - - // 设置超时 - scheduler.schedule(() -> { - CompletableFuture removed = pendingRequests.remove(correlationId); - if (removed != null) { - removed.completeExceptionally(new TimeoutException("RPC call timed out")); - } - }, timeoutSeconds, TimeUnit.SECONDS); - - // 返回最终的结果 - return futureResponse.thenApply(RpcResponse::getResult); - } - - @PreDestroy - public void cleanup() throws MqttException { - mqttClient.disconnect(); - scheduler.shutdown(); - log.info("RPC Client disconnected"); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java deleted file mode 100644 index 89569b0c3d..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/config/MqttConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - -@Data -@Configuration -@ConfigurationProperties(prefix = "mqtt") -public class MqttConfig { - - /** - * MQTT 代理地址 - */ - private String broker; - - /** - * MQTT 用户名 - */ - private String username; - - /** - * MQTT 密码 - */ - private String password; - - /** - * MQTT 客户端 ID - */ - private String clientId; - - /** - * MQTT 请求主题 - */ - private String requestTopic; - - /** - * MQTT 响应主题前缀 - */ - private String responseTopicPrefix; -} From 9f3730d5d943e4bd30acfcc21223a43791cb995a Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Mon, 20 Jan 2025 16:31:37 +0800 Subject: [PATCH 086/309] =?UTF-8?q?[fix]=EF=BC=9Acode=20=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotDeviceDataSimulatorSaveReqVO.java | 10 ++- .../dal/dataobject/device/IotDeviceLogDO.java | 5 ++ .../dal/tdengine/IotDeviceLogDataMapper.java | 19 +++- .../device/IotDeviceLogDataServiceImpl.java | 6 +- .../iot/service/plugin/ExampleService.java | 86 +++++++++---------- .../mapper/device/IotDeviceLogDataMapper.xml | 6 +- 6 files changed, 77 insertions(+), 55 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java index c4f9d1f55d..5e2b085437 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java @@ -14,16 +14,18 @@ public class IotDeviceDataSimulatorSaveReqVO { private String id; // TODO @super:不用传递 productKey,因为 deviceKey 可以推导出来 - @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123") - @NotEmpty(message = "产品ID不能为空") + // TODO 讨论: 日志记录的时候要记录一下productKey,目前是前端已经有productKey了,所以前端传入,如果不传入的话,后端要根据deviceKey查询productKey,感觉直传是不是效率高一些 + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123") + @NotEmpty(message = "产品标识不能为空") private String productKey; // TODO @super:中文写作规范,中英文之间,要有空格。例如说,设备 ID。ps:这里应该是设备标识 - @Schema(description = "设备ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") - @NotEmpty(message = "设备ID不能为空") + @Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") + @NotEmpty(message = "设备标识不能为空") private String deviceKey; // TODO @super:type、subType,是不是不用传递,因为模拟只有属性??? + // TODO 讨论: 不只模拟属性 @Schema(description = "消息/日志类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") @NotEmpty(message = "消息类型不能为空") private String type; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java index dd811ee323..609c9c43ad 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -27,12 +28,16 @@ public class IotDeviceLogDO { // TODO @super:关联要 @下 /** * 产品标识 + *

+ * 关联 {@link IotProductDO#getProductKey()} */ private String productKey; // TODO @super:关联要 @下 /** * 设备标识 + *

+ * 关联 {@link IotDeviceDO#getDeviceKey()}} */ private String deviceKey; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index 42dcc0cf38..40516c7944 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -27,6 +27,19 @@ public interface IotDeviceLogDataMapper { // TODO @super:单个参数,不用加 @Param + //讨论:艿菇这里有些特殊情况,我也学习了一下这块知识: + // 如果使用的是Java 8及以上版本,并且编译器保留了参数名(通过编译器选项-parameters启用),则可以去掉@Param注解。MyBatis会自动使用参数的实际名称 + // 但在TDengine中 @Param去掉后TDengine会报错,以下是大模型的回答: + // 不用加 @Param在普通的 MySQL 场景下是正确的 - 对于 MyBatis,当方法只有一个参数时,确实可以不用添加 @Param 注解。 + //但是在 TDengine 的场景下,情况不同: + //TDengine 的特殊性: + //TDengine 使用特殊的 SQL 语法 + //需要处理超级表(STable)和子表的概念 + //参数绑定的方式与普通 MySQL 不同 + //为什么这里必须要 @Param: + //XML 中使用了 ${log.deviceKey} 这样的参数引用方式 + //需要在 SQL 中动态构建表名(device_log_${log.deviceKey}) + //没有 @Param("log") 的话,MyBatis 无法正确解析参数 /** * 插入设备日志数据 * @@ -34,7 +47,7 @@ public interface IotDeviceLogDataMapper { * * @param log 设备日志数据 */ - void insert(IotDeviceLogDO log); + void insert(@Param("log") IotDeviceLogDO log); /** * 获得设备日志分页 @@ -42,7 +55,7 @@ public interface IotDeviceLogDataMapper { * @param reqVO 分页查询条件 * @return 设备日志列表 */ - List selectPage(IotDeviceLogPageReqVO reqVO); + List selectPage(@Param("reqVO") IotDeviceLogPageReqVO reqVO); /** * 获得设备日志总数 @@ -50,7 +63,7 @@ public interface IotDeviceLogDataMapper { * @param reqVO 查询条件 * @return 日志总数 */ - Long selectCount(IotDeviceLogPageReqVO reqVO); + Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO); /** * 查询设备日志表是否存在 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index 769821e3e4..85f950d5dd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -55,16 +55,18 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ // 2. 处理时间字段 // TODO @super:一次性的字段,不用单独给个变量 - long currentTime = System.currentTimeMillis(); +// long currentTime = System.currentTimeMillis(); // 2.1 设置时序时间为当前时间 - iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now() +// iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now() // 3. 插入数据 // TODO @super:不要直接调用对方的 IotDeviceLogDataMapper,通过 service 哈! + // 讨论:艿菇 这就是iotDeviceLogDataService的Impl iotDeviceLogDataMapper.insert(iotDeviceLogDO); } // TODO @super:在 iotDeviceLogDataService 写 + // 讨论:艿菇 这就是iotDeviceLogDataService的Impl @Override public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { // 查询数据 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java index 22ebe8b4f2..13810bbd50 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java @@ -1,43 +1,43 @@ -package cn.iocoder.yudao.module.iot.service.plugin; - -import cn.iocoder.yudao.module.iot.mqttrpc.server.RpcServer; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; - -@Service -@RequiredArgsConstructor -public class ExampleService { - - private final RpcServer rpcServer; - - @PostConstruct - public void registerMethods() { - rpcServer.registerMethod("add", params -> { - if (params.length != 2) { - throw new IllegalArgumentException("add方法需要两个参数"); - } - int a = ((Number) params[0]).intValue(); - int b = ((Number) params[1]).intValue(); - return add(a, b); - }); - - rpcServer.registerMethod("concat", params -> { - if (params.length != 2) { - throw new IllegalArgumentException("concat方法需要两个参数"); - } - String str1 = params[0].toString(); - String str2 = params[1].toString(); - return concat(str1, str2); - }); - } - - private int add(int a, int b) { - return a + b; - } - - private String concat(String a, String b) { - return a + b; - } -} \ No newline at end of file +//package cn.iocoder.yudao.module.iot.service.plugin; +// +//import cn.iocoder.yudao.module.iot.mqttrpc.server.RpcServer; +//import lombok.RequiredArgsConstructor; +//import org.springframework.stereotype.Service; +// +//import javax.annotation.PostConstruct; +// +//@Service +//@RequiredArgsConstructor +//public class ExampleService { +// +// private final RpcServer rpcServer; +// +// @PostConstruct +// public void registerMethods() { +// rpcServer.registerMethod("add", params -> { +// if (params.length != 2) { +// throw new IllegalArgumentException("add方法需要两个参数"); +// } +// int a = ((Number) params[0]).intValue(); +// int b = ((Number) params[1]).intValue(); +// return add(a, b); +// }); +// +// rpcServer.registerMethod("concat", params -> { +// if (params.length != 2) { +// throw new IllegalArgumentException("concat方法需要两个参数"); +// } +// String str1 = params[0].toString(); +// String str2 = params[1].toString(); +// return concat(str1, str2); +// }); +// } +// +// private int add(int a, int b) { +// return a + b; +// } +// +// private String concat(String a, String b) { +// return a + b; +// } +//} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 1554b7d1e0..4a60f8f78d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -27,11 +27,11 @@ - INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time) + INSERT INTO device_log_${log.deviceKey} (id, product_key, type, subType, content, report_time) USING device_log TAGS ('${log.deviceKey}') VALUES ( - #{log.ts}, + NOW, #{log.id}, #{log.productKey}, #{log.type}, @@ -77,7 +77,7 @@ \ No newline at end of file From a85890d958417e90564fe4ab075fdf6216cd1c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Mon, 20 Jan 2025 17:03:27 +0800 Subject: [PATCH 087/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=B7=BB=E5=8A=A0=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E9=85=8D=E7=BD=AE=EF=BC=8C=E9=87=8D=E6=9E=84?= =?UTF-8?q?=20SpringPluginManager=20=E5=AE=9E=E4=BE=8B=E5=8C=96=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=20ExampleService=20=E7=B1=BB=E4=BB=A5?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/UnifiedConfiguration.java | 6 ++- .../iot/service/plugin/ExampleService.java | 43 ------------------- 2 files changed, 5 insertions(+), 44 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java index 374e3856a1..e02ec9be05 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -5,11 +5,13 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.framework.plugin.listener.CustomPluginStateListener; import lombok.extern.slf4j.Slf4j; import org.pf4j.spring.SpringPluginManager; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.annotation.Resource; +import java.nio.file.Paths; @Slf4j @Configuration @@ -19,6 +21,8 @@ public class UnifiedConfiguration { @Resource private DeviceDataApi deviceDataApi; + @Value("${pf4j.pluginsDir:pluginsDir}") + private String pluginsDir; @Bean(SERVICE_REGISTRY_INITIALIZED_MARKER) public Object serviceRegistryInitializedMarker() { @@ -31,7 +35,7 @@ public class UnifiedConfiguration { @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) public SpringPluginManager pluginManager() { log.info("[init][实例化 SpringPluginManager]"); - SpringPluginManager springPluginManager = new SpringPluginManager() { + SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { @Override public void startPlugins() { // 禁用插件启动,避免插件启动时,启动所有插件 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java deleted file mode 100644 index 22ebe8b4f2..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugin; - -import cn.iocoder.yudao.module.iot.mqttrpc.server.RpcServer; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; - -@Service -@RequiredArgsConstructor -public class ExampleService { - - private final RpcServer rpcServer; - - @PostConstruct - public void registerMethods() { - rpcServer.registerMethod("add", params -> { - if (params.length != 2) { - throw new IllegalArgumentException("add方法需要两个参数"); - } - int a = ((Number) params[0]).intValue(); - int b = ((Number) params[1]).intValue(); - return add(a, b); - }); - - rpcServer.registerMethod("concat", params -> { - if (params.length != 2) { - throw new IllegalArgumentException("concat方法需要两个参数"); - } - String str1 = params[0].toString(); - String str2 = params[1].toString(); - return concat(str1, str2); - }); - } - - private int add(int a, int b) { - return a + b; - } - - private String concat(String a, String b) { - return a + b; - } -} \ No newline at end of file From 3647fd36860035eda823fa8207b5bc6a312037b3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 20 Jan 2025 19:28:03 +0800 Subject: [PATCH 088/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E7=A7=BB=E9=99=A4=20ServiceRegis?= =?UTF-8?q?try=EF=BC=8C=E4=BD=BF=E7=94=A8=20SpringUtils=20=E6=9B=BF?= =?UTF-8?q?=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 15105 -> 8535 bytes .../yudao/module/iot/api/ServiceRegistry.java | 37 ----------------- .../module/iot/mqttrpc/common/RpcRequest.java | 39 ------------------ .../iot/mqttrpc/common/RpcResponse.java | 33 --------------- .../mqttrpc/common/SerializationUtils.java | 19 --------- .../iot/framework/plugin/PluginStart.java | 20 ++++----- .../plugin/UnifiedConfiguration.java | 21 +++------- .../listener/CustomPluginStateListener.java | 1 + .../module/iot/plugin/HttpVertxPlugin.java | 8 ++-- 9 files changed, 19 insertions(+), 159 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/ServiceRegistry.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar index fa75769049a1e0036436209788e16c216edd4891..8b5e72b4a4a93219518d66cbf05cc05a5e486a5e 100644 GIT binary patch delta 4241 zcmZ`+2UHW?woai41_&KQs(@5!p@ky7BPG;GZ-P`o=@Mz7C80#BB4X%WP!v&-CcPJt z5(Gg&M5=&*aFP38-}Bw|X04fT_L*;=GkdSI_Ss)#;0HA-V|_3QCE)i}kONDl;-wszoKk_?Ihcr`_*->3SE#gr0dg>N+1TZ;D&TN5f2awLQINIn+6Kv*u-HvB#SDNC^ z+`+FNk?5M+%~|`hXr2CDFR<;@x9Cxr<9-LBISlvB$o+w{N~-Z@>vY{4X46Y-Z7~_% zj^vH?@-C7dCQDwVl`5NJKM-!=Dtj9ke*3O10W+cCt;gN2 zx&rSA;9r3v&m#Wk0X{`%AF=<3fVTr)1fS*xe-|inh7~}mGn@y?p5aB1BI&u`*{cf# zUp+^A`hryMBXI+r0|1Pm0Dv5R1IURtl!2m*z3$xb@VVmb9CyIPTS8AW{#qt^FQM$Qa^_ZjKVuCUXkL3-Wcx z1iB5Khp!J8c^oSKVzYX7b@A%*$a?R$E19;zrYMJj?0~mDhqeP9#V5J1s6Lq-^~8yw zT=Y9rpF$cSs|Laq8V7+K=HKEASse>|Y~efZR#Ghfd+z8jitqjMuBJTMxk6)oleeW3 zo%1Msf!jXHNDJtKUQ<;c>V3>QpFx*|Hbbz&)J<|n@(CI?c7>q89_!1#yRd#8RO5o=g6+k*tMJtFDey2 z%5pB<&J7tHx=kk7o6BF7R!P%KI#N+yKVvMlw4kzX!g2dTjOBW!Nu(ZnF~6Z3#mYfF zecpX*kqlkz*54Gi7fVy*`>5*Me8$V~3yyt(6;_KgBcC^=sxA7R%P{s4&Ih|)1HQSh z!d#E~V|zw8-zqEJ+V1b{+8@o{-+!L1&%EO2cANKjxOBi*NepBv`WZ-zb)Bv?C& zMQ`eJAO%(!6x@V=&?ZTWE`R$hQAq5m{q>d0@?k{kMtD4%*+PbHZgYYl?MPtE6w>wC zT)7!`{+=VE=%$Uqix@yF?xeVww|CQ#b2mvQ=$_+?6QWR2k@mv+2bbGVk`9uXFXrpq z$8ETpBj4|iR7&crZOWhfih{+tgtN!7j*IE`)wIkHXf&)9NY1BRb21% z-UTA45}auhN!RZMjd8tecL`@3FjbzI&hD^yv>VF%GKTg7u4f-7u2&nJ*S>ZP3a1&+ z-+zl?-T<4zXPdC7z}|C#{t9IX>n~G74k_DEng-<%Ng6SeR;IZr{|d?D`DK({BW!!? z<9>qd_QqCA(*AYJbvx8`0R`^Qyw?)4%1mfSYD3%t_pX1bnJiB_wu%{wDt$jYX@nl}BYl8lZ?1Sh zJ0`Cun6X-dVL;EDpy79e#6xF{aHPT+J2)%D>=o4#8+xsmrO9RDC2X|qv#8&rVk_5{ zm4@y(*^QzY`<|EgW>Yema92cc4oXzAXvWrFmhW?UcyU;ZKW7|HKoh98WSn*Cr2VJ8 zR41Iehx?lCPys3-QEbys&`dlT4{_k!8At`Th^mbCLJg!?d!LW3gbU%HQ+h{|feaSC zo=m;#Uw&O9CdWx3=%CLyPKYv%sB6kabs2%mRwzmByrNA-{j5Bd1c7rWfq1?~=h!`h zY4U&dd3w`miThH<>Q@I}ol2(R@)r`*O*MJFT|tSoD09oiNCyjF*Ul;WQTj{O)jb%b zD)9-$yK<&LNm_Erw_PyyoZQj)CDKN5W`y!|nW!z#XXtESW8?`mtyV|r0L{GaJC%($=_ohG<)9tI_Qa5N4kB19( zdPCYuc(B$x$S;p=j?SxBk7yor7yb}M_H4>T=pP2T=f%<<5adT^M!c?-YXt2yG2A`D zy_Fdha#CdE%{SVa!nxbnK9}!Q9yS#BD?{T~?cH-L2&^&ukT&WX7FFT#Du6`y#eR zsEA3O-JhFW43ld-SD!gZQR8JFy_X3Ms~=H+2_B$ba-Hub+s3XnqX<&ef^uj}^ffTL8zxpgF5gQSrT^vLvg+xprl5$6RIy!S z!AMv+%UQ^xm%b8>NL%o%k5gVCv}cXDQ7Zyl310f~aDjr6S5E`!W?kjtcz}DF%-3@2Pg_O3F>Z^*1@-luv2;q3mysniAFq#gSa`A8Ab&q%y zN|!D51txV9Ii75MYvKQPO$ceJ6F8$2^19m`ypXwI`00mK-C#30g*M6|AxHj+SwlBBs zCbxv4mLY=Oi&4RHC5>sJK_>y_ zgULW0Ij9=ODZGK{)M%G zcVpMRWZ0gCOfNsW#*aAqoU}A6n{w%WI=^C}BdF*Ww`7iecKKy&2TNEjvXnQ?KNqIm zeqOL_Yf3~{-U$??!Iwjk?>z0zBF%%6etPgW`J}PU2=j z%EKoJSOUZ4LgaYW2Wl3tp^J(I)NPOOICvaBia*)zxvutjt2gGs8>ApXD7~UWJ|+C% z=K#vS>2T-RdtZr@x^!AD8?oPs*y3MlY-VlgOTWuSvF_JOt zCab96z`e@n(RMv}>YcSs+(`*ParEh)3UV8ES+Akgw*_Y~p;}^6zE8DJM!B+9m^>@D z5gw?z$1PV{QwFV&j&`Q6wb{*iGg#A_bVHFY!S1#*33YM4O^OiT=r0?!L|L_}_nm7} z11uWP?MMpwq|=Ac{aVY|IPF`-RaXN4eH%{+&+2=@)xgI*h2QpiWec)u$2Fb`sP zjjftUkGh!S*p6~?^)qx4zafv6=plnIrU`V?=vuk}58YPEMM%2R#XJisY|d?&oc!bu zZsWg3npdaE*LB5gBDTNgai{>ADu9%4Bx!K@(~#xm+F=~FGqIO6pK2NnyzM)4c=Ir9 zB&d`U{2B~N=1$rqPPL(ec{y=|{$z&*@@REHZPub}$J1p7r3e-#Micjq zlnK&k0PXxGOKJ)0|Ju5A)kkjCI)lB_!Z1@ciW8T955^qytnODYLfC?TqfCuLy6J?* z;e}+ff{*>4n+8wSEM{Th>R-hKi6BnZN1TeBgwtCOfX{;)ps0FODO@vPv6yS5P=O<`P)z~+)AgpnUtsgKjJ7}r|vci-rB@}RN3 zz2fIhC53){19D`w31DrJKsTn-Qo|etgL{(5T*ci{$6NIZr5O^xrs=R-?Y6-}S1Y9y zREZzzPk_eM`alo^_>br@>=E0D$UW#6asUAJKMucx&HwG7 khWCThoc;gb8}EOZ1(6`OQ=%C)Er1qaO$q=kv;F?~FHk~mHvj+t delta 10401 zcma*N1z1$y7B)@|-QC^Y-KlhUICOVOjdX_$-CYXO(%oIsD2#w0Qj!A72Y7$p>;2#7 z|9xkkIs43d-nI7GYu7t_ttIJTZ!~o!SU6;;$19?R;WZj9T6hbCA!W6G@Z^K&`D<}9 zkcu-b6>9j^v-Mz<7b>fAsVj8*Mb)@=x+^rUu{tnO`5NxG$(doz$$Fm}^78|=^TfFb zUiPIhU28bL*mO*9EG3%f$Rjni$Hxe6kdfAz8Tsjj+g{Nq=cdi$cZzDX?B4utd#s4r zMaQ;@n?_>7$LI_O)-g{o+xRCn2Zrj7r~&d}a0og)j`14}=&4@qNHqK?C5DbvY@@gC z1`O@}Eb`~@o%D|KAlJocbAy^%C*2xx_ZGmO)A4Tp>W`E@Wj0lj{#Dgmqa*s9pHTm9 z?L#xc?9k+}kEI$k{Zr`+&H7Z9L-W8scmuovE$~zlz=+~J`N#0dQUAIzB;!!fTevuT zy1O_!TDf!lW&1BA?E{j=$<5Q#-PPhhU=LON1!H-DG5-U5Fl=pS^M65;{y{t1SvhsC|B3X2WJ>BXo%m`;N!L2t4gF7 z(D1Y*`jjd3{3`a!i0Ck+YZ>2@%N*=jIZTh4+gXCMXLwZ#nJ^1Df|CfbrRQ3nzsg8A ztfDqp7*rP?b_YfmvJWWHcl8C92MkRaCmHS&#$w1V$KoC6dmp4o#J(aGJGjbW0d< zM0~@njzt#s782V;YV}dAj@Lq3#_nLEIt#EcZn!Q=>W3M>a4TJ-h_7}7nL|=9$&RY` zYv7=#cXgZn+GMXCOS?b%Y3)4gdWWn$*&S-4Tv-Ef8P_jGE6F3Bi!_Ju=KL3lS;8lw zL(p{bemBUEH*h!S9-0nxvs!xkPBjR0wea(x_=??(_xE)^veV=;6*st2Fe^QaMK;5;OT#1dP@atILo zqT(X|fYK;S0uJMG6XYou(ewwIp8Uu~i;D&=L%ubsGPLf3daiU^upm{}2! z4cH5$1tnx+!`QeD>rxBLILY8EIU?)C@3)1tFvMaJ))@D3{bzKu%<-)ZmsffbQbA?P zX@{B!Es*nBIxXK263+uRuwpn*#enh}mh&2WG1~O}1uhEe8x)i2NT3oTm+0v|Zss?) zj^$vJqJ=|iU*;2A0s3XO?^ggKvb~ZJFj4{(ww}VZ`+i*9VX=voVi=?WLEG{{rO zi#1OttFRBkG2D8nY&`&$pfm&og95eBF-8QV2$-t5du=LmdwrM_FE{Cky2BJ9!gz4b zh}}(M)NKer#i7jb^*BI?I43D)!eJ_w9EC}Ft@G-UmmeYV;1=c$-5xi*#<9SRgs-+`z}QPE0R+E^ zpdBG+*I0JzFm1(EA0JPyT7Ex1qvsPEXyr!NrGynEML%_!p%-q$ctN4ZF&O8rZCY^SOzt_9T=)D+-=CX6eDtYusJ@Dhy4ub_55bSxzVo`u zY`T_KBD>tmuAx?ot5S04V2R6@Kk~v6VfT>tvu=jVJX}x5oj+}dLdyG$G8vBd#N)2- zS(MxcV-rv6!n((<;rc}wjGl{tlL{JgvYsk8gSA>Dk*WLZw>GP&IdElGILN{E;Fl$s$q1n53;Y8Xm zpPjNMfr4M0^M*yvS+u+9V5Il<(4KV-YgW+Ge!8Q@zr#6mv2g2WWBnz-Jg_^5n??XE zp^8p@Uzf$N%TNdkBi6d+lEGFhJyFKG+zLHZu|gfBcaX@Uc-3|4v#m=aj9*&o}z}8Yed-WC9&y0 z7h4Ai;KkT8BS9dv1Hr5VS){V@wKpu^O|jpVovcAfXX*@IE#l0;fBz!?cBqVWrbuzD zTM6uJ__a?N%1T@fySp37jyYO!gDo?o!dv2$Zy3tgOrcFH?GBMA(Ugx5z^Hq4G*DAb>?Bju$ z&JL2eay*flc^3F(V&%t=gEb5&U+7H2FiJ;BT4;?u%34b#dytY6eyh@_TRD0=1N^|q zVl53NJ~-ll*-!*|n&1I9Tb`~)$Q!dLGJcPvU>d793h$rOw-RrhEytp|&s-C#N+ zzlCW;Mq_XAW3-k+l3uaHw&BdccEa5d9~cz+GGPL9IETNiA;7_FtyCJ%Cn%F9&$#qV z!lCwTO1O6_GJt}k21z4(RKJqSNVPUIT3vdiTiN_VJWV}>12hIKp~2LQyAN)~WTJmM zNj-LF?ZFIAc!ARIXyGaXO~V;eqhqvej}{BgTBYO6xeBiz4_LZ%6unbKtjwq{Jwc4K zt>GE_s+#pB@7yHT263XSen~I%WsQXu;`z2?@B6#@(GAf1Ug6;D1D;qt-csl*q;rAf zns=LM&h8hg_&yoqW5#X-J6I9@1I|p#biFXdFdS=`O{>Nq`Ec8Ez|PTrshh~?FwI)~ zCArC`a6bvA%+)Gl4BY z1h$lplb{KJBb@GPMRhsxFwY^`ho!l_5X8Yxr1U35aa*y^Fi)vuJ3ZZVhh!IIz0orl2XX7kb{mBi2j7QdbJ5{ScfSp z>l;QBP6MZvRZk+I{5fL<0n*DVDsQHTNai7IdLaVWSZ38#X7$XWRp$eDsI;}wn+!)? zuEbh(y}D)TLGU8;iB})&XJLV&VukYVo|9pv6xdqrhIBp|Lr}hb9<3ou)bv;5X%Ot= z%s_hl6Y-mOe#hR0e&U@!<)SNI0-&IXA9$z8;~e=zSAUaRl?O`vFi{?O?(a#`q_^RL zqk}6Uuwf~%UxviSX2N0ylibJ@OIg9BQ}|uuxl$Ax<4W9EgD_%!? z7;9eri`m|t8=HH$erI`@s&C4c933{+XVPbkPZCB9yuPs=aS~R0-fvd9Dmz#t{2GWt z7J}%lviq&)plb9n0^!>yYx`O z@1wC#0WQ9=JbeU zQ?ZCG_U4mW=LFCj)NrzHabLI1m>u1JiQZ>}9R=+g4>n%Aw;SwCWobAS*nG8L!?p%< z7+$F?$u1d;*Mq{Tlmi0VUZS-dG`&BMU56x4`I5EHIqQv$bCu$;-0%0%cmzeqsXoK;TiyTci&(Tlc(dE30X8rv|HbgIdt4}j*VKZBnAaQ*s2Up5Tpf!E8C`*y+u)s zT*dMAo9aBM+IE)Nju64^BC{-H65mSMU{96677?s%L-pjNqstM7e0F%1?w@} zmkulp=3P(SLJy_6ub|-8Ci>>@#fZ3UEx6i%eMBlVN zcA*dksuunfH}s!1Z(%40L0{6469#O~CTm4tgr0#6I6{is9!AlKgzSq}#J7Yf0AjpWa; zs7eIEcAH(3I&SzELgh^>gD{@k`S1FucZF}yTu!(Yu%?z#^dIC!N5YQN8IR3<;~`uu zDX%@is%XeMCq$1VQWzh_u4ZC;E>v00d4pJQsHt>8tCGHhT^&QOm5fb0nl%UJHBwEt zowT)69D{pq$2Jf4><~3B()kd<^c*euWC0BFtj}Sa6)v)3vZH-AW={i5Zaj>S|2PyF z=KvV8+7XHyDd>2GM1*}^TuZ>YPceW0l~B=5h&o)>j2c#Gf09}VT+(|<$;A?A4$?KhY6^};_EGnncvJJ&SjAxJB+d{k_REix?)SKV*vQ;F?&Y&9;m(v$79}+Y**1f6 z&ZS3H8Hpki(zQ$(KOmL%&5YK63*T0Kb}6k@hh~FQ&^53hb#KmynhhVj7 z3GfyuUiwTIFH?Vjd{$Dy^GxON#Jl14C$wY&d|Sq{I+T{Vxyg0`V+f5shZp_nzUu&Z zv4q9$nfYtxgx5Gj7V!K4_%Sq!3q94j%n;2C; z(r}~gEFePkBOZCmTaE3R0=lMWk#_O`ew`*=8-fg?Jkv5I!+t+~{@N`R9ad?{f@RhC zdN^r<+9_CAw-?aChfYGk{RH$TkPSUQqdvR3lOZhCbID;Rq8NuzYhuql zfsPfDnD|E!Yrio1$py@GWfr@zlG@aXa`FfD zKu|_*lhmNRqoN24UQbi&yJzUD)U{mcr)5C(81gugHoN5@M{6wsi}o0@7UdURY`OHv zr5U?>TdG8t@+i34WYyvmuq4{N-+Q>Tx6-m^>726D%=Nx$)`=X(I2##8;DbfWpUd$W zR(!g=!BCfo9t&X}*AMPwzARnZpK-ixu>;B7Ehp*%^s0RESEoLcx-iFkPozmr+Sju> zh2hm4IRUp2v`J+n8ALlXB!N0EC@4k|s(2=Xv;~%q8@j1DCtsj5=k97_Xx{#Snd{J6 zb@QXa$aE812aX2)lm(pHB*ql);pr33N6=~|?vl>tO8M23O}@x71fyl_o8T3UXoK9) z@>tY~X*g_QljD{m=~f8djIqs^*V2z>_MhuEcW!_ecj4t?B|;LOhywfU4-!Mc@W@jGcekEs-_as3T#w4iH2|1av%4%d!i$Nqm&#e!?RURMu)SO*?7RJLL0m*6Dp$di`?JkXR zU^7R!i2_uZ4ZqlUN1h=$!7Z`0NDZF37E7w2yJdvLwDeD=y{@Q~>o{?-UJ>Am-1r6` zz~eH=QE)vKpJ2f?zd7=3xQ>FOj}6)MEriauvS-Ss7Wq)A4gbTtB|*J5I|2|*iX!!# zsu%eCn$2)4onofHDRxN`NAI}Xn-IMUUpi+TeW5FOwO@5nr;$4;vP$jle(524lg8Zx z5RHA?K#f+8Em~HXeOl9ENRu**xci9dI5!*M9gev_hyKQ*h#<;iF7_1+-r zKH+eIiWU@?5=12bShcwMA`OsvvmCgoETms_3PMtq_23A+%u6K>XDT;gmtY<*I?u zOrVk)P0}PZW^y@0#)q{<;}W$z=f!>dJiOR_#{=8NLx5FXz! z?{42Bj;b|TLluIa6)?zmgzpUZyAT(J)~TSlf+(Sypq8-^+5611cQ8M$Lt;1}N{uP0 zoNzU*JsVD!BZZB;>)OmidM7#nGp$pv0GStdiwhF6MC&S%qp*Qp*O`3Is9H$o7!=_`tgYpNjG+X(zb zo j+eJM@)>qijT{#1_T_{u&D9KPa1)Im8cnn95fR^&}Lw_TrUMccRDH~)0KPTpg%vl z+jo%jwQJIf2sWq$I!H8F&?o+0dY2EQ_|BZp8XqhFP*eQCqpTk zTG}RqM&kaqY8-Nv2%s=K!;t(aKWUe)b!jifOL+ax#TC7&x6xGpy82{2NIK*mWRrJ2LY@MqzH#20RT)^>o;m zR%PmJ%j~&QJadHxo{Da?6sIVj0@JWedZbg3sDjw#=%loLRIiNY&$<`7AT`-+KXEm9_b8STO+-^piJZ}a1anG`2c~qA~3X&+|rwE%) z_?#AUNRxqH-`6b+?Ry9zrf}v{<7lU|O@;AsSXHE`U4HOov|-hF z;(5nnKL1-<{??1@`%e?=W`5K*`648|JH91v)fawfP%l;%5hh;CM%7{OFcuMHUk_UA zOeiRUgq({LoV1T)5381`*w6NI>qW4<%7SVXg-VTiTkv)O$<}FjfCb&vB~~BU*8@K7 zb0Vw8N9qGR`-#~+=ycvE@LE^~(sDu-^rjOEu84QphH{>uzX=iD5ISMD&G}ziGW*jj{TEcsG~2l5U{G0AXT@| z5-?f4akSgmi;-=R_I;V~&4o-tmxn&RtHoy1m$UMw6Rsuh)DPucL~ibQwfiMjdPs5x z1ZP?k1$CmlPbqT^gdH#b1fgx4hO9_V8W^L|x*h(E(neJc;)$^Mk)i%(A-pKl?{-oN%ebi0aqJ#>wF+CdtSHi#9>L42q)`U>)4Le3%}HORTYcB7i0h^J zEvuKF@`5^Mgp8t*t5CqpuO9lijHJVdATehL+;?h_DHWhb4q<1XqCR+{}ufy9|PH@mr^U^IonXho)mI!wS+~VbEyN(_E09mJS z=qx+GN^`2E6!q{OaB;^lZ1}++t3rO(e04M_*kl!lTL9NzT`VjDkYU1$eG9r6LF%6H zqkdMTG-A+n(GA7dww)ZCG&>You0sqkU9{q230k;Z`PCfOytcs*F43$+ngv7k{&5hw zzTf!I>+ghJ22nJ@kcMmV+oRzt>yE~}IjChcij`KxNqwcQdUF4nsLqAeNLA!q(?)3p z66c{preGcpvy@rIo%8mnENAa15VBvO8-0?uR@X^C_M%vHeuJ~cbt;s|)_aTVz>g&v zTij%Og}lOwgjbsOrnz*^>{JNbTo%2#VeEd>b^Ft2vJ5bXl*lj4C9WN8T*BAA4!UcUbHuwSdTo8Zf2dk##7lKz z_V@Ya_Z!V>acC&0)Q9sKmWQJo6*+Aww&$wy93HOjcFs0zzvGWy?m$mF7iTsnD^H*$ z&=Uw^xA$;y?o6pugq6aKxD_3A_j0D_(Itt+H5Y#FcBBHauW!iQ(2_x{keh)}zCcC+ zM8{JSR#OiRkM5kats7$kq+)57P(>sfB+j@dJeA^JqV+u^afv@{iK}zQ&#ei;7yRJR zRDCgKR&}_ij2EMXO9*qSiVHttL@2M~TC@X-r6>UqRD7-PWnnj&&ilj_#VJ#ApzVpQ0?jPFCZs0W1a4x>r ztSB9cJrU2F$e*_6m`|M5Ii-{@=B}kZ<&!cY|B^RqmD7QqE;%Zf(}mYX{Tg~KEQfag zYl zvL)26aV;624>-&qD7;c&Nrw@t)ar>i&MG{tfJkUc<{fC~elS!}(CeJLxlToE09_D%ljp@ z-gXMmuiI&fk3TODB)U7$irT)%vkukBleHEt_6j=Ee$ z=4qJMSnkp<@{2~LLu>dWtli{%&a?frE>#h3JA^{%c*vJc62_9|CGnuoJ!dQQ8Q6{mUVl)B89-n^l4v8oZP)BL{im$52li;lm}&OjZX(1+EVA%D&Z6q6OqMiu+$n?00TM8i*AAdfrsNp3A_s@N z?bHH?vipzzE+-mHJjZ-r22(f`fX&RsJ#m`jL*gA4)){VOa!v|dOT-#ufS=vxH?Le3 zsCiU8bf3pC*KZbU+>Xug5_$$`2b3?|+yNP;lHpQqYHiJhS^N0-^( z`r}+@H)ne;oo^0~9ZqiMJ^)Ab1x|TKPTdd1B$EnPWHA?29WzvfU1ZG?y~5ev&@ZsQP-cOxuJzWXO+0cbvOVZXQ2nlX6yL4ZbJ?~R2SL3kn)O5- zjcSF5UJhUnXWceL${rS{B~8M3-L7GXLFb*9{Ka^a_w+aW76-q16-$sr;9@KOX8m7f-E6tbcZGNu9 zPTIW-=J9UMf9XD21y{x_T6l9D3GLWD;W|9&`7RwK#*+-b|C8$SJ6vVj%e)o{#v{=K z^& zRLaH9jvvbkYu^kx^R}8e2soEeSAvE?!G-;kZUnv};r%<`2rNj-_mqO;N=kxH|FAt- zI6ow{K*Ri_^q8$vNJ;{&0PZ9uegdwNlEAn^gQ0P7z{q5%Pa+~R5}5o)5iwZ(LG+LT z12%gQeRvd+gR}l6s(cV(z&wez$*`#ZM}E(rWFJhZLn-wqsK9?j`Tpy@{z~Pc{Yy9X zNtgGZv$OwHd`!>zgMD~-+=_o+kCiSxDT@4~_{6lnA>MwKJ_z&h}z%AscPxby4Uiu3tM)+8-(w}z!?{L$9 zSNzvu=U?_P$RF+fJACy&4?q9g++A2R*F@~tPj^^alTV}R|E5U@pHGC>~9 zF+6zbffM|>N`gHf#t+cd)zQx4iMO%8baMPh`w{iuqvhYqm{9lkp{(E}3bLp9QcFPs zQ_Bh_C!qo#QT(1lKOaPgk0NofJ|!yU|A(0Td5gypqQLe5lm-t`Vm$TK)9Uj8h<^Ag g{srvgdH^cpt1AH>;^, Object> services = new HashMap<>(); - - /** - * 注册服务 - * - * @param serviceClass 服务类 - * @param serviceImpl 服务实现 - * @param 服务类 - */ - public static void registerService(Class serviceClass, T serviceImpl) { - services.put(serviceClass, serviceImpl); - } - - /** - * 获得服务 - * - * @param serviceClass 服务类 - * @param 服务类 - * @return 服务实现 - */ - @SuppressWarnings("unchecked") - public static T getService(Class serviceClass) { - return (T) services.get(serviceClass); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java deleted file mode 100644 index b2a9f03607..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcRequest.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.common; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -// TODO @芋艿:要不要加个 mqtt 值了的前缀 -/** - * MQTT RPC 请求 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class RpcRequest { - - /** - * 方法名 - */ - private String method; - - /** - * 参数 - */ - // TODO @haohao:object 对象会不会不好序列化? - private Object[] params; - - /** - * 关联 ID - */ - private String correlationId; - - /** - * 回复地址 - */ - private String replyTo; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java deleted file mode 100644 index f3225d08e7..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/RpcResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.common; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * MQTT RPC 响应 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class RpcResponse { - - /** - * 关联 ID - */ - private String correlationId; - - /** - * 结果 - */ - // TODO @haohao:object 对象会不会不好反序列化? - private Object result; - - /** - * 错误 - */ - private String error; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java deleted file mode 100644 index 620b007635..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/mqttrpc/common/SerializationUtils.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.iot.mqttrpc.common; - -import cn.hutool.json.JSONUtil; - -/** - * 序列化工具类 - * - */ -public class SerializationUtils { - - public static String serialize(Object obj) { - return JSONUtil.toJsonStr(obj); - } - - public static T deserialize(String json, Class clazz) { - return JSONUtil.toBean(json, clazz); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java index 2cb688cfa5..96ca833690 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java @@ -1,22 +1,20 @@ package cn.iocoder.yudao.module.iot.framework.plugin; -import java.util.List; - -import javax.annotation.Resource; - +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService; +import lombok.extern.slf4j.Slf4j; import org.pf4j.spring.SpringPluginManager; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; -import lombok.extern.slf4j.Slf4j; - -import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService; -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import javax.annotation.Resource; +import java.util.List; +// TODO @芋艿:需要 review 下 @Component @Slf4j public class PluginStart implements ApplicationRunner { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java index e02ec9be05..150051ce58 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -1,46 +1,35 @@ package cn.iocoder.yudao.module.iot.framework.plugin; -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.framework.plugin.listener.CustomPluginStateListener; import lombok.extern.slf4j.Slf4j; import org.pf4j.spring.SpringPluginManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import javax.annotation.Resource; import java.nio.file.Paths; +// TODO @芋艿:需要 review 下 @Slf4j @Configuration public class UnifiedConfiguration { - private static final String SERVICE_REGISTRY_INITIALIZED_MARKER = "serviceRegistryInitializedMarker"; - - @Resource - private DeviceDataApi deviceDataApi; @Value("${pf4j.pluginsDir:pluginsDir}") private String pluginsDir; - @Bean(SERVICE_REGISTRY_INITIALIZED_MARKER) - public Object serviceRegistryInitializedMarker() { - ServiceRegistry.registerService(DeviceDataApi.class, deviceDataApi); - log.info("[init][将 DeviceDataApi 实例注册到 ServiceRegistry 中]"); - return new Object(); - } - @Bean - @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) +// @DependsOn("deviceDataApiImpl") public SpringPluginManager pluginManager() { log.info("[init][实例化 SpringPluginManager]"); SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { +// SpringPluginManager springPluginManager = new SpringPluginManager() { + @Override public void startPlugins() { // 禁用插件启动,避免插件启动时,启动所有插件 log.info("[init][禁用默认启动所有插件]"); } + }; springPluginManager.addPluginStateListener(new CustomPluginStateListener()); return springPluginManager; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java index c0802d7f57..4542868b03 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java @@ -5,6 +5,7 @@ import org.pf4j.PluginStateEvent; import org.pf4j.PluginStateListener; import org.springframework.stereotype.Component; +// TODO @芋艿:需要 review 下 @Component @Slf4j public class CustomPluginStateListener implements PluginStateListener { diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java index 1d6fcad92b..54d9c7c2bc 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java @@ -1,22 +1,22 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; +import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPlugin; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import lombok.extern.slf4j.Slf4j; - @Slf4j public class HttpVertxPlugin extends SpringPlugin { private static final int PORT = 8092; private Vertx vertx; + private DeviceDataApi deviceDataApi; public HttpVertxPlugin(PluginWrapper wrapper) { @@ -28,7 +28,7 @@ public class HttpVertxPlugin extends SpringPlugin { log.info("HttpVertxPlugin.start()"); // 获取 DeviceDataApi 实例 - deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); + deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); if (deviceDataApi == null) { log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); return; From d608c4b9844af070a2e45be6b6f20f5c5e4e3660 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 20 Jan 2025 20:02:46 +0800 Subject: [PATCH 089/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20HttpPlugin?= =?UTF-8?q?=20=E7=8B=AC=E7=AB=8B=E5=90=AF=E5=8A=A8=E7=9A=84=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 2 +- .../yudao-module-iot-http-plugin/pom.xml | 2 +- .../iot/HttpPluginSpringbootApplication.java | 2 ++ .../module/iot/config/TestConfiguration.java | 34 +++++++++++++++++++ .../src/main/resources/application.yml | 5 +-- 5 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 0a9d0bf454..01e2f14547 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -67,7 +67,7 @@ 3.0.6 1.2.5 0.9.0 - 4.4.0 + 4.5.11 3.5.0 4.11.0 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml index 22cb439681..4658a1f6bf 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml @@ -127,7 +127,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java index 6b553f92bf..2b871cadea 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java @@ -5,7 +5,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class HttpPluginSpringbootApplication { + public static void main(String[] args) { SpringApplication.run(HttpPluginSpringbootApplication.class, args); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java new file mode 100644 index 0000000000..b32a1f59fb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.config; + +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin; +import org.pf4j.DefaultPluginManager; +import org.pf4j.PluginWrapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +// TODO 芋艿:临时实现; +@Configuration +public class TestConfiguration { + + @Bean + public DeviceDataApi deviceDataApi() { + return new DeviceDataApi() { + + @Override + public void saveDeviceData(DeviceDataCreateReqDTO createDTO) { + System.out.println("saveDeviceData"); + } + + }; + } + + // TODO @haohao:可能要看下,有没更好的方式 + @Bean(initMethod = "start") + public HttpVertxPlugin HttpVertxPlugin() { + PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null); + return new HttpVertxPlugin(pluginWrapper); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml index ea2234f83e..9056af48a3 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml @@ -1,10 +1,7 @@ -server: - port: 8092 - spring: application: name: yudao-module-iot-http-plugin - + # MQTT-RPC 配置 mqtt: broker: tcp://chaojiniu.top:1883 From a152f6d98fe7295f8abd6b7096b388f0ca3dee42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Tue, 21 Jan 2025 18:18:28 +0800 Subject: [PATCH 090/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E5=88=A0=E9=99=A4=E6=97=A7=E7=89=88?= =?UTF-8?q?=20HTTP=20=E6=8F=92=E4=BB=B6=EF=BC=8C=E6=96=B0=E5=A2=9E=20HTTP?= =?UTF-8?q?=20=E5=92=8C=20MQTT=20=E6=8F=92=E4=BB=B6=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=8F=92=E4=BB=B6=E7=AE=A1=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=20EMQX=20=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 8535 -> 0 bytes .../yudao-module-iot-plugin-http-1.0.0.jar | Bin 0 -> 10643 bytes .../yudao-module-iot-plugin/pom.xml | 6 +- .../plugin.properties | 6 - .../yudao/module/iot/plugin/DemoPlugin.java | 77 --------- .../dependency-reduced-pom.xml | 81 --------- .../plugin.properties | 6 - .../yudao-module-iot-http-plugin/pom.xml | 157 ------------------ .../src/main/resources/application.yml | 12 -- .../src/main/assembly/assembly.xml | 31 ---- .../plugin.properties | 6 +- .../pom.xml | 2 +- .../src/main/assembly/assembly.xml | 0 .../yudao/module/iot/plugin/EmqxPlugin.java | 7 +- .../dependency-reduced-pom.xml | 43 +++++ .../plugin.properties | 6 + .../pom.xml | 70 ++++---- .../src/main/assembly/assembly.xml | 9 +- .../iot/HttpPluginSpringbootApplication.java | 0 .../module/iot/config}/HttpVertxPlugin.java | 7 +- .../module/iot/config/TestConfiguration.java | 1 - .../module/iot/service}/HttpVertxHandler.java | 2 +- .../src/main/resources/application.yml | 3 + .../plugin.properties | 0 .../pom.xml | 2 +- .../src/main/assembly/assembly.xml | 0 .../yudao/module/iot/plugin/MqttPlugin.java | 0 .../iot/plugin/MqttServerExtension.java | 0 28 files changed, 103 insertions(+), 431 deletions(-) delete mode 100644 plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar create mode 100644 plugins/yudao-module-iot-plugin-http-1.0.0.jar delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/assembly/assembly.xml rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-emqx-plugin => yudao-module-iot-plugin-emqx}/plugin.properties (55%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-emqx-plugin => yudao-module-iot-plugin-emqx}/pom.xml (99%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-demo-plugin => yudao-module-iot-plugin-emqx}/src/main/assembly/assembly.xml (100%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-emqx-plugin => yudao-module-iot-plugin-emqx}/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java (82%) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-demo-plugin => yudao-module-iot-plugin-http}/pom.xml (73%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-http-plugin => yudao-module-iot-plugin-http}/src/main/assembly/assembly.xml (74%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-http-plugin => yudao-module-iot-plugin-http}/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java (100%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin => yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config}/HttpVertxPlugin.java (93%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-http-plugin => yudao-module-iot-plugin-http}/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java (94%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin => yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service}/HttpVertxHandler.java (98%) create mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-mqtt-plugin => yudao-module-iot-plugin-mqtt}/plugin.properties (100%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-mqtt-plugin => yudao-module-iot-plugin-mqtt}/pom.xml (99%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-emqx-plugin => yudao-module-iot-plugin-mqtt}/src/main/assembly/assembly.xml (100%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-mqtt-plugin => yudao-module-iot-plugin-mqtt}/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java (100%) rename yudao-module-iot/yudao-module-iot-plugin/{yudao-module-iot-mqtt-plugin => yudao-module-iot-plugin-mqtt}/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java (100%) diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar deleted file mode 100644 index 8b5e72b4a4a93219518d66cbf05cc05a5e486a5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8535 zcmb_>1yqz<+xE~%$j~9(f`BvT#bS>K7-$W{Qhy{@>@klOM*i| zRhCQr_b^OA{$-eP#bIvm<=-+$007aS!&D?x6=Y>VTAV7f+A)3h9e07y?byAWen`&( zg|t@C4*-eutCP*a;>SBt%qD$% z`S{#N$_BJ7nH>NZosRaQQd{_IR6nPV+e{`pipTu=x9*SSYg;mbZ+o|Epzm2Vi6`GN z@$uCC=uoy*u{hb^^PZV)^FZ0s$m&U7U4fk+($AUyS|F6mnVZ@FQJP-^uzv(tIhZ*> z%$@%s5dVJ%dbvTs4*w8N_E+I{4iGn6^M625{sYR&!Sx@I4F7_3v~_!IbzKO|-)<#e z!9JT!3;^Ka<>c=DcLegTu8un9&aR&FV0(zIxihDkE!f4SMOWXEsE7Q!x7(m4c~HHf zu}h^bPV>t*DGYiY%)&rS9RgX&&h~Jz=DAraD|d_34Sd#QGYpnafv1#=!G^;MszFgL z^6wQ_=@Olps;d+W_bAxCR1&i_OJmv|>#iF>5(gBfhFu+w{SPj_$zAOCT+E+x1oSwG zgaO!yw5&@c`K7kP?icl_EcADt#WRR9k(C{jC(&g+Dhca`TIb}+N~j~SEOA6YI`eRl zdz#3u6IbQjZbs&jvT7uKmS9U1xC`$_EX^`Z$VLA7II{=Y7o~k}hOTDU+~;e!P_gjB z&UG&aV@R0C(5elnCXOz}BaDv;wzErIB9j>y1D(=Cz`Nid%rqS`vhjB(M9Cjm_;kQ| zG8>sJ!*iLdBR^{_jJe><<%R1{U<6CK+SPO^vAu~MUUoV?piCCto(6WK;3O)Dhz$e7 zy9pw-_d(PiG8h{5m9J%OHR1XQ5@|94PMN`+jW6KoMvGS(4|Kiz-93GS-9OYZ+DV9p zg%a6L?c8nTu3)jP%Yxh0N#7fN=V3_39F#Uql}tz~gbgxF5e0V%7kQMPsKMCcr zrYINAoaX3jU@MCmkXsA2hH8ky0La41Yk1&1J;j>=R;kBXri+-MqM%OXG|HZkyVt~3 z7)c229Umlq$z({}EMMMB@C2LYLy~1~NiU)F2M(_}VzwjogD;j|jPhg6x(G8%bId}@ zDr~=~W-kH+QvlA0oKF(mdw1#nOuuGtf52?9hJ|KFsp^I!PK}KHh^E2UCjQq{23a*h zsJ=v7Si*d+d67#?=1v-_FAZ@pjXdpH3hAVz>K`{zXuE<^PW8>hKeq3R>NY9)OtM!%6lKB6oU1acT}P z?cj7)no_eCf+9s=+ZPr#ZCB^b5oR$AT`t4jam~FOxHJfm76UJVnDF@`Pk4Pr2SKNT zXytoCiu>V@$!3%F@h3X6K*Stu&dB+*LvBxVqoa$vE_w*zg4H|a?J;lInoM!yZSpCUN24`Y5hIT85rFLRSy|)u z+qgFkp++<--4MNvPiDUE(YYe0bQv511sx1PL#{-6O05iw8PSy0Snyom2E8esTuGc) zQ5czhun25xvOK4}QqvlDdEz80aK!qAGRv$a{m=@JI3enwo~J0CdYi^VkEMK^9_vA0 z_Vf;NtN2%CH0a*r4f@(BcGa#(*ABk7WgY5HusG2knK!ECo(k0ABAmnmUiq)`mPA`6 z&-B~c!7~&b6M(@@G0a+8&gF8f)}p1&6LcfH#`y5&k2_Vdo*lz68VlZvGdFX!q#6oe zNKg4z>1P&wc?m&C4hy_3cmhH^cT9bMcMHeJJu;lg5UeNC*0HcPDnPk^2fX6>jNjsg z?D)!7k~wx;LbGO?)mq$<HS6>jUAZ5$@a2PGrz@qkws(YgO`fU*c8NFF@tWuzk$C%Z6TRQNyTm`AhyO; ze}QpYM_Z9q$OZo~J2YA38saszfmB53CI-SE>lW#vLM z1`FyvoQtxbOVceOOP`R_?3eI`mxo8cAm1M^1JZ;rjwyrv0l6r-L7??galP9)9$0cH zLfrUOSh%&wP=j)o-Q6POEIZDC(DJaH0A(bGN=3vi^^Sg`3IN5Y9d`wz7s8Ad2PI5m zo<2{h5391uXVl_8&G^)PHp71V*5??Ky^~oJ&Gy|rqg&*JHyVj%K6xF1p{Lfnptdv4 zLN0)B#GqkcbWCD>M$>^rfj999;3O8!s#$LnH{fi^Y$dYJ=}j~OW*Igm?VF3no(W>_ z{3`4g^?VHWE+54|Z}^mol4;231Y6;ph2VtLTOaLI5BSZNKdTdOgK?|mmDF-MQ3;kc z@p`y*ewAff{eA_+pr^rb2K*e3#^<=m=>$OFpa zo}9IeZSZ6xDC@jFSnA2`DQgM}|Ik@vYhC$nkM(dqeU5DB_&R<1Sl;dfAUm{dK8D8ByN;|Y#MDf z0X~LD;qa!X>N}>-2&lvtSx+v;&88K+DacirV(nKk8-F-nWOXL`?Ur8sgVhJ?6WfEI zIg^b%HT@rrq`I^YoEeSu6klY%#C@-EJ`j2bqSPB(gVThwsmxv>ed0CSvnViZPQIjrPny23cl1jcM~{r-kFM&t)AFm zsRx_hVB*m+C6*eKw1-j-t@D!WoT11~(P@uqgYy`a8Q&8qlDl)F=|)>BhT&OBRDE_! zZYj$bkmaL!^Qbb|nfw?`B}Lq~{!F;2l>I!#taLxqYjn&N{q|rcb5&d=-XPjUMS0_b zI`7(w__hYMD@CyGcCyA(Md)g7Q$IQN&3SCgy;XE*vBhwk&v6J|kwaG1=jEgqUsk|F z?iG5g3lkr9d8>7X&B~%o{LD^|`bHcwU;3Dz4~Gm)(6rtcbJ`#7?K_!FJvnJeRU+MR zv~XqoK3+QFAjXBP$?*XRKiYi0R=FH-YbjZsA)mwzhn*ipPViJKu&9ezut-{XrPBO+ znm9W0G--NBFb7e86cO4?gdIWSz|7N3MphZAU3I8e^|+NRRV=Vc$Fy!LTVs?Gu&-}K zj5jb7B8LfQ56PWxBNL0A&X?$*PA!9Bl?X>ynD9#qcUAY{bQ!dWwPPC?632Z@WqkaI zPRv9$KbV<(Xl6h?jr``SiN9t~V@MK<42QK6wE^n}k%$HRA$}we$NJ|F+=Yt+wckF` z>Sp-G?)Zk?(ppJU$ZQY4jX&WYJZE5Dzf`Ujy&M1r6C*3jDx#inxjYo zj{xxVi$%7gBDt0AX~?C5IgKQx%#}}o?U-vDbRSJr@+e8}-oyOF7YgyE3MGHTr7#5V zSRRpX+A82#j^zSYMnx!`I3cskaLov}U@Z6!^Z&h`e$k8MXDYF9Uve_i|QPQp)DWbs1`BNd|_u3sHfpEjk@FUQ+13ac^Uw1)#Z;fc)pP5hX(aAdU zW_%HhPXQY^f!$TC^~~rw7XGMSKCmU}~x z49am?=Cn7$`OtjaJpnWMI)3MQTZtLd{_&x9V;6C+z+3ZqP!js23>Zr00!~GA5pEei z1stbX?u6-92`$Jut<;T&2%Rrz``OJS=kkZr!D*%<9;ZX zwRIz&KGJb8@0}x@BxIR@dOW0S-0nT} z=Zl8nzg#RFLop71aEI(QNJet;G=BY+f07ebz6@DmkXmQEiMb(yHE7;K zJ>q5Ru-y&+VuBM%SnLj7#P{)ngF&y(68dQU1A~t_hUeH))e|zO{e_1d1_QeSeoAK^ zmf0crr}_6L7ba{~%cVUI+K8T9z*+@H*&wTL-b4o(6;dIZ4>n!Y2Ev?i7xNc*pzoTK zlkdv61s=Xb6$etap_f9wGGZx!`aa!AcoIA%2yZ#ZLscz+bV?# zvldLetZ^rJXD$>L;L*KugkNpAyh^*!lq5Jf7>TDr zY%oSGTh%gFV$BgvTIQX2g>!n5(sf-W)>(xJ>b#xL9n7Cq*$i~$-52D&UiE9EtR%0f zyg$^Cult&^XjLnzhx0>M zNWkq75tp5-Xk-9D;$L*dRZHTBmH;_ATiHK0b#QQ%aCEe_G6TC>IoSUY7$F)1s&nE* zVh9Fi<^;LhA!yi)By^F(7%B7;jGgpolR5B5{O;%;C1Qlpj}_1KE5z@dBKZ<6!-Aw^ z=$E=$KKP8>J3c%(KcRW;VcV6`ZX4W0@^S2uUK74QDp&CESAA~=-k#AJU4teR=-Z=3 z+74u?IIAjU3+VWszaJ|O*U%_wTp%o4NKlx|x60KYk~j=9n_sfjDp3|Pca0*8#5dr$ zvqUJ>Gn1jqzcU-5xkz!Aj)6r}$C5G`a0r=j->&Slrz>M?N((zMde7w3V%48IruyCO zmCZpze+#h{9gCuC&qgq~N@;@o&T&FgajSA+3#Oi8f}{rFtt~5ljQZC!G6?g-E+#+<+OEY?P;xOPSDD1V-fBCr)64}+BP3PiEdCSXTldVcVNT3)Ece! zd+Sf0qKbsAA1$RLVNbI(EBnu_kbnI;EH*wYjU#)OK~GTs7VLq9TN+cs8biWIikS*GO9^j*A)W!5m~@xWGccU6vwYeXkc<&wjwz!FP}P&U%HA{ONwV;SO!1^0KVP?0Hpqp%#IFroSt^J zl{)RPIRRkHwl1b*75&ywBP)pkn#_0}8iDImfk=iW#| zZGcjM-j)qw=F8%@3*!Lrb>&{*sv)0HY`MIidT1+ftKMsVJxi4tbp9c7?VVr@Lw6#x zXdxK6$cc_8%_Oy)CAx>qr`Dj9G0r)YPp%95cG=$C9ffG{*+ zwSwLk^@oMMwn>}_yFr!c)$mBP8wTUHc8STz1M!6uu@oA-shYk?W;;eHT5z*7D;_2s z8Xw5~%(y{8?hV2&D!s+vc0OBTMa8`s-_x%n{w8f_2jA^>c89oVzJK5RK+|@yKUXa= z329C2nrW~!WtUNC7u2@H!GUzvb~ZmH_OT}CxIGe3*$`wr5P$PvYZrD=!n`bFrS=HUU#XCYK-C&Gb$i$yG1|61Nj}Dc47gCs!ZA2s5 ztP_S4u&^(#T?mXWh*l>tt82wbLrnR}-HwZk&f9c8Ur#Ks(YaXk=J%|QN%bC*;U+8( zx+{_x^g2qjr(VNQ%A@>3j@Vvpc)eUC1la=kCdq*G@CoAw*| zJk_L220FeDF2_LCu{Dw0V7r%vR@xp^<{?!xiGh@9;4Xhtb4L}(-N*N$xd+gNSL0ZF z@f36+NM{xs<#$l~61i*&Zx~|kXCr2%+@w*!BlT zCAjP1k7K;(@lx)8neY4dG$3PW$sP!&YI3O_W1kNa9z}tf54|O`ISP&Ib?tkD2qq1# zyQej)J9>1w)bUm@NDUR3qu9KJOATayXSQ2U9_yW7+ZY~G$H&DDZ6$?{LnA`ywc#jocib&V zE_W)@Kt5p`RDlODH>r5NLAE%R#ZPhaRC-69eBPUtydn~d(~TFGHWm^x5z5~?eV0?c z96-R)RtG2P`c{hOcm_g8x5 zs9ggje*muiwZ8{mIc(QJoBs{`>9_qo{K|2=hNt}%{15-_?^&)KxNDZte_;9H$NfFi zl_Ph}gmo!oFAMjx4nL;z`}JHg{c)21B>ZcK?k8ozQ>Fio@UQ*4>jnSoHr{ob=*!#j zzqsx{()_up_n%q*Q`BE)Sta=AEdQ0n|8u&(l9t!$hA$2A{}7tj;9nnsDl z3&ekVh+TH1sQO{L7a<8UV0<$qoSg4@-g#`2YX_ diff --git a/plugins/yudao-module-iot-plugin-http-1.0.0.jar b/plugins/yudao-module-iot-plugin-http-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..a1d4db6c8a0d94ffe5ca6535b2008102c4d07152 GIT binary patch literal 10643 zcmb_?1ymH+7dPDvLrQnYfJli*gLJ11Fmw#vASu$_At@mUNVlZ4G)T8}Neg_quIsM5 z>;BL8eUCG74maMrbLY-K(6qstBvJyac=A?_x+$ zz&o*YF_-1ZyMJ3lLqXyFSxinuURpw2MU_oX!YfM728I(Y@GRQ?xh@5MWoXDWv>qN! zu9g!yZBTg%oszc%ysGgDIboK)&qmpNxmh%Si+XRg63v76w z=TnvW!LW*z!=NA?2f8*F#{ue4fEAi)eXC^2i=m=PH+#xPqqap3&Bq8rJ)I^pPBNUF zC;emnW9IkK`^VD0WL$H)KyGIUj51fGJP+ENKMj#LIpm!~e-8u{6v|KWg1rlbk^*UTIRNnztE)q4^1Om z8xwQWUl^eI4+9QhduMZ_ADc(|E!L6VRQAtcprB;op`iHx!@}=dPz5_UivG46Cwq{i zxvdSGkrl|nAzjs40apae8y}?#5lOwotR&MTD-O`zgsXe{Z zQ>@!~Zwo_QpJ&3o4;q?Om6u6UX9xFo`hmy&d$VWcPg|f}F@!_0gQ^}~aG|vYeYkm* z`%bh4_kK(@Mc$#mA`iH40k?eGu}{o_S~Olf2(%tWZr3uP*5}jU!vNFHar1=9T%RK> zfxr?iSS?%H}|SHj4z6_ePv6_UdJ@a zBAA*e7=_o9ItERlH`FI!Ir{SzoA+8Sy@;MyA_KKRHl2}loizi1m>}p#r!c5~tvfoQ zXnd@Z`eD$e|3F``5e#Y4B|E&vww5l9j47z{pjDJC(Hb2UO`*?}IkYKibFxc}Cu`tP zUcg*h9Qitrn{hUZ9MJWaw~4l2WXgar6n&B|bQ=v`C$^x@`O}%)f+d;wV^cf-Eba%z z!E_2A^KC0S0k+7ySpjL!sn%IWXV4mTNnvDJC!e1Huo1N-=yIvgnBvi{^Jy>yX7A4D} z@Tm}ku}IERz79S6dz4x`S(^Jv*V`}Afn0PPO>v|HIEZ1P$ADP27( z9bEXxLjJTHZv`nJnX4h9FMR-)M)A#IbGLBcYsj~}t}iFVV7|-iayTd`!T-y=rs4dS z*(EC0mR~cwTF#-zt=jZs$Jv=`tf~hqUZHAWj3!e@b<&6Z)CQh8`?_$K)JH8BiFm39 z{=%)~w1d{La*D-pn5QPr&g;{r`lnZsZx)~gyXymqg6O?hrPqodbr9_~q#Ab0!7wb%3+cs61wkS&qC6-g>*u{UG|xXiP5`Y4ip?kLQv{6==(h(W zOnNr36IEPF(XH_qj->^S;8P05Mvt)*W~k@;7m#roZ{fuuwu>X;+ut`SU!=fH1XaiI zvv7lHP7#_Sf@d0WQGy<;TnGxTmOnY8HlmYz4su|kTXT}>_G0}Uje_CY@9{V^>BBni zLp)O_qbWY&(Q*q!?Qpe5GSt#elEc#B`B#<4wISR1rE-;(PBCV~4iH#gMi-tBXr1|d z3B3Zr;9Q*)19u(1IIG!rLZ~i8_&zI3iw}Fz)P;Ehv6oa+jLyusT;vXqo0r6M3Ab)3 zfd~zG2<7a~#vfxx*aP+D%oP=$N_ZFGWZP*wpfdX;uLwI0zB76~asU_G-lSD{W66&@ zkY{TPp}NL*~0CyezXcVLJP;y<*@U4krU~ z<4ABfqka+V3bb!{RHC%;!G{11MVGg&^ge0k+_ zx;^P&N;hr%cADv;d37+&V^FB&xY6`z-bb<&rWWxwE~>rkz9B;j5ogY<^5u%sxN7}? zneK*|V_dL#r6s9M(@KYS-P-5f;T6&pO3Fx3mbnu{`Kc2kK)}RY;`zuj8yTW#sf{y7 zTmXDx32JrO^zge9;vo0|yY}IFH~-j3a=xOa$|Co)4VX25vyHJ?Gw z8quwz5P5M04O5@o^JKj>by6Zytr8*&WwuV5va5Lw;EhD5jTF}7L{=9++CiNxCA(Pc z9ZuZ{vPl#4*e%(3gC05mz>9{@UXagm=`vYGtkL_>@#sYkxvtw2SKFL0xi*E1*@`E{ zSgGaWZ9-^ofw?tdGZ1!oYSm=U3vs#$l#JxSf_5*QYTW$?)O?bgYt}MviCn}Soek*0 z*=x<5uQ9-a_I}wtM?8c3*xea}yda?efT&cLXMyu{IY|N~0ffTD^l#Lgs9l zQw{OgEIOt;uj1wOGxqW?l?wD3qg^q1?*(cWbfkIHntY6a%)9~%1`jEkBf+$XJjvrs zw?Rg{QRtk~PszW0W~+dODFV384F?%9Skfn#ekw2&_mcoaDph(shOtA$tdUIFGGbEF zD~oeA86KrSU=T5?rDo0vWBY^#WW9l;&B$MC*53CFhJaOdAM<8Wwu^-w^@cv=*<*B7 zc`=3z1M;U*eW}vvAbMW1gkLpkji3HWM&_jsDl{mgnoe3eG)JPIcs$8xbags{2z3#g z>3JdPY*-+ZY=J2uRl(D!LxyIls(Zt7GbL-AEO`P>83Y#}HJ?mU?K#P7REe6Iy+^um z$(`|@>9iiP)Ye;e#zE^O63U7c|f~t_XC%=kHr-C$)+j?O;>he#7o!zD=WOyLp5Sh3?7vI7moVk@x<* z5MUB9Q-G2jom^rH{Fg0vW) z^I#dfEI$Y*kBW8gsGSl+FXA5K;3R&i6JG256nCWulPJN|tXW0Uw|4?+P&tlk=}EKW z&3ay9AgX?%9T|Zba-p5lV+Z=Rwt4x!9e~>ARtrt(HShb+JC`bt_ZXP#QRKq4{6r)a zG9FwR8L4cnZ+4EFt?Ob5GJ6!+6c?ioAfBbN{NM;*=XsTj?Fu|PZoHZm`f2GJH$^W=2z zEXzz)JtG`HEzi2$IENPnxrB5a+;l{p$ILJ3qgEyc$-QMJEAAn#llG%Rsj1#9>}S*A zbE=UaKfw=Q56p)7!fky?HneH@McMKF)4p?z4C=0@fEB*XQ$!+aw%s!^qbL%z%f}8^ zI5o6i;w3&|u2CWTyjqV=k1y4TBnp-0^jaIXO+HV{=6x~WLHx1`4M_14Fyeq=e3>Vb zm_hrI^)csee|AlLWk@Cwtra0H0q1^WpT2~Tf2E%$EW>a<4=7^fu+7pgFrs|I+OyYvB> zCk1|dzDVKG$c|BzYyQ-q5O7={5L^vj&BO7KEEU5W_fuWsvC*r*ed!EvCUVLV*t9Id z(J7>zh(**XCq@izD^FO@3@;r9WS}<>zs;hGAgh3&54;ulAj`H%?GCjgtVsm|?0$~) zScKXQDNsdH5#Dfr^wJygZ3ma%5Nhr7_cwNxw5z~FpHK>TF#aBGqktY>g-b+a_RyN? zTE`c{=2hmIeM!3;282`0r9+6*IM{QID?+zxvWR^}XZ%eh;>2i0sN(aUY>6Z3(EzBS zvnsexQ?@Hw9K`em@}4}fkKewpDS|^FvQ9)Fb z?{`oOwSw8pj?wj~`EJ_- zowH4sbLyJ_;fA%21A;H;x2<1p|G3kC!{hzs+Q-#*jScF%3%$sHK%uV~=o|b|f!Led zm>SyJI*LFbR^~>3x^Cvh4A|guVs+ozj9S&hX)AwJeuyD;Z{*%Vxa>z6ZsqZId?vp( z-&mIw8$IXU_m;3;Eb^*C2T+2lF4Cej8oqW230i(8=9d7lNkZFtzT#LltnYJE?jZNpT# zXa^mU)1D%$pyUxb*FTMMD-xj|c zwY&bS`S87c@drSb0@)Z_f$hIt(V8@@A*vcg-h=Ba^DK#P){0RB5#Z{jrK<5{D1GxO zv5>5g=qNRdt9cuEQ)Qjc*v{tBb0?TItJE2TvxBuQs|?3s7_BMrn7!3r-`Ml7d!BpHqe+?&?L*pBRebzZCrZ}UNvtBy}8}!ErzOIbU}fE*eM|^fmFik?$j|5y-WZuUhmJ z164TVoZS`^U62j})A-9rfFx2Nuq#8izOPKcv1@k;cTy$2-nw_;qH|=x4U<=VUMC5V zE`kRt4C+jx-Cf86A#itXohA*cF3vF_BF(4ax9^tYcsS`Okd%T{G|VGwn9I0uNP6H_ z%1DHa9mQu}r&*fBgsXS0;mj$QYf~gunX~Tg-M=)Y@RNZDING6r(A6|Wzr3xVS(zr# z6vDrCo}>d}8nnfP5P$q^_ffPZZ=iwub>|#hZlBC&)Hq2dln1u=`iUYM;_o>D%g9LZ zW~m`p#-7A`of6P;F3x=FUFd+=!xX->!Xv4k%ZFPknA8@2gT+Qi^4EKXhsC3 z$8ggtWGZ7WnlV6{}&MKEmjPWwj4P=bN zHCrghy*8W1+750&N23DEg`;yNIlx@O=iR#am_WXZ@Qh(*1*@F-y#7)U7y$bNy3p`lBbT85l5n|qL5AB zQulXrk1Z-md&#t8Z-&O4dL-#yRR(n*68Yj_di(C~Bc8Kt4=o*|WF#4`*N?N7E=hZe z7$oGJ5#^A;$c>Vr&>hO@cXlKB#fEmj@DEvm>Em|~JF@AM-?ph6*Bg{ikgZ$GSFxF@ z2D6*abUvx^Oz2Zog|Z380#m^Bg&- zWH~ql+PSZi_oA?tL2r50w6{Tn8~5U|gVd|WYIgH;D0Q4-XJ*sxa5|NU7pPs4@jS{Y zl4CY>!1|pDm~t5Oxx>Wq_a4fvdj%&AfxXN#K+9ir69CeVjM~Tj*0n)bt5BK)5^8xJ|vp+>Y`W zu}*e3>SnK-k%QLZB^838$aIh6qw=NEjRpS0j)P7WAE9H{I>Gs)CDYPorxRD}y2agv zw58)-kLvdwdi>^?YLN8yp2{|j1O8ACd-e6$p|G|7SgIR#k!O*BhcQx#cAhM2yzgzL z9_pU6E)72EU*591DO%LHVMcw?m*jwrig-@_Z05-CrPP!Tv=3_+jt-BqedfCK{%DKilN*Dw>24DB94B?%PBi=l|-E2sD3ee^W z5P&r+L{anT$S9%|gSQB6?-bRfhY+P&$POAxTAQ!w+ARadgdxc2RJ#1ZB^qV1O8Xd9 zXxUZjS-JwL{tq|Y(Y8#FCW&ffQ1|Pd_2FWW)SGK1nY^X_~Ea-zI@(sdcZ}w;# ziivQ|z}?vy@zSQ+SR)V||4F{$3+|#qFIKX5bFOIHkhuEH4xtBKRV!om2&o6?%~cr^ zq#5ld&Qq+eWM}1W6D#n0O~F@g_t9&EO3u+)n|!U$sY|Y36ds%Iw5AHCfw)(!wQotB zSN3PG5Dm}iwITlO_<#~avIQnCw+urWP68V`%tGArn0^~h|BOr;GD_@4Mf;csEL|-J zV9&swnv@p#rQ-fG?xy2HCii2WG!u(`DukySDQyfb;t{dB0`la8987`pZ(4clibpoI z6&`1~Fu!M<%*e+@XJ{5FkikQO$A(`xy_x8H8h$I0<9A8Ikrg(x&V?LVxM#ALd4Tf# z4VUm)C;w%seF-p=tV%``*~_#C?CF9vv`>HCmNNPBHD9Pf24zcH(IDme(K;` z#y%*heBPWJ5yf;`0If_*<}}=16K1+?#D+Z^-iltNiX44lM|7+a`K-m+-@e9>vA#}y zA4J5KXZlj%CGSh}XI3%q2)p{NddL^X>uq6TWt{wqj;u z=;ycPbwst783i6Y z0%c@SYN5V^yuVLBzk2#(`e_Yv2LE}o_w%Tf{O`g>Hf+BS`q;i+3jQ<=`&s#G&;KV+ zKP&$>0c8C)(qp|F&apy%Yq_$T-CaNau#EKE1Q)r@VCw4b=5p`u0HXhomLay*Y_8T; zCF%natK3-4Ln>m}lT&kVVISu_Q8y##kZCZaG3XdiuWb-YpNigfJbSiYCMk~;(D4G6 z*A<#AxW-qH%wHz0!+3Xv?8)`PCH_X0<94-MQKW$+sLY1p!1W#l;8P zZyts|#K@#GwPWmLm2)c7*ip{2)Q(D|qOIrDw$#%%)Qw_Y3yy<}h>l|RyzXT7^ffWV zA0xJS0)E3q9Y!n;w*Z0)uH}Dc?TX*}X1Eznx2e;nMzn!GeDA{Qe)mG7`vn-5&rsLA zO@)}wfS7D zyvPlEe2ivC?_8|kMDsL8+Bv8*gH`il;+?i*YZh(~7q&AOZmx|E)}LqITx<{Vw%nfX zziEL}8a+_QH=4|5EX1RQUr?Vydd4#7lkLGUmL$uW=n7rab?q}dz{k$k-5|~_&^XZyqU4r`Ka3{?U>SxN7A##Si<2k)J@lJamn8Nv* zi@k?ls_VYgx_u$!>9=HUe3zeI#zCH(ahGgC=f3^`Xi$!7uYG-9^BT?O1zG~jD^8Q? zN3V2o;jbw-gI+JrdR{Uyi`cLAX`90!iwJM|G$WIhzZk%KO~V&}@`?kXu8P?SF;G_I zYC;LecW*HMCP{2bAYf z#^vw#<&B2<+>aixFue~tEhkSG6WeyL>614e={ZTy)@|uT;>#@*l78k`=FJt+FncA8 zR4v~yg|rDPxGYEq7E*d>?)7LWA&LZV=h5COPfDAIBBDua;p;j=qk%(ZoaEAJ=cxlf zNOeH!DPvfZcR6Hm00Xbd%(>a7$se(!H&Rp3vN~_RpssFWFL^ExL(X&qfNpRGCUco>-Wzb&Z6WHJzWrVwztxu8#=u0kyPERs z?jiI4p|(KmZ6S99By+F>dXFqh8wVED2QPWf4^EG9gw3*;39=V^^(o}Khzo#}@+eJB z&AZ#BQkwn-e&+s?n$fDf_X9jA4lriR7c#HrklBW?8&42GPlp-pOPbZ0dXG@?&lug4|hbkzjpWiga6}Hf9d%B?B{1d`@>J%-B;l!c>6>7 zpE2q;Wh|)M+nYP(KPt_4#QGz(e}dx3^~hZR|e~ z?e}f}Gp7CSEZ4W~|IyR$2=~X){`rUQheI-||BTZwkM`B!kDon7S$Kr6w(;)%yzk!K J-dTr&`afenk2e4S literal 0 HcmV?d00001 diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/pom.xml index 4a46b61672..949ff13e6f 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/pom.xml @@ -8,9 +8,9 @@ ${revision} - yudao-module-iot-demo-plugin - yudao-module-iot-http-plugin - yudao-module-iot-mqtt-plugin + yudao-module-iot-plugin-http + yudao-module-iot-plugin-mqtt + yudao-module-iot-plugin-emqx 4.0.0 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties deleted file mode 100644 index 5a67270bb0..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/plugin.properties +++ /dev/null @@ -1,6 +0,0 @@ -plugin.id=demo-plugin -plugin.class=cn.iocoder.yudao.module.iot.plugin.DemoPlugin -plugin.version=0.0.1 -plugin.provider=ahh -plugin.dependencies= -plugin.description=demo-plugin diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java deleted file mode 100644 index c97a5b9b5e..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/DemoPlugin.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - -import com.sun.net.httpserver.HttpServer; -import lombok.extern.slf4j.Slf4j; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; -import org.pf4j.RuntimeMode; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.InetSocketAddress; - -/** - * 一个启动 HTTP 服务器的简单插件。 - */ -@Slf4j -public class DemoPlugin extends Plugin { - - private HttpServer server; - - public DemoPlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - log.info("Demo 插件启动"); - // for testing the development mode - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { - log.info("DemoPlugin in DEVELOPMENT mode"); - } - startDemoServer(); - } - - @Override - public void stop() { - log.info("Demo 插件停止"); - stopDemoServer(); - } - - private void startDemoServer() { - try { - server = HttpServer.create(new InetSocketAddress(9081), 0); - server.createContext("/", exchange -> { - String response = "Hello from DemoPlugin"; - exchange.sendResponseHeaders(200, response.getBytes().length); - OutputStream os = exchange.getResponseBody(); - os.write(response.getBytes()); - os.close(); - }); - server.setExecutor(null); - server.start(); - log.info("HTTP 服务器启动成功,端口为 9081"); - log.info("访问地址为 http://127.0.0.1:9081/"); - } catch (IOException e) { - log.error("HTTP 服务器启动失败", e); - } - } - - private void stopDemoServer() { - if (server != null) { - server.stop(0); - log.info("HTTP 服务器停止成功"); - } - } - -// @Extension -// public static class WelcomeGreeting implements Greeting { -// -// @Override -// public String getGreeting() { -// return "Welcome to DemoPlugin"; -// } -// -// } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml deleted file mode 100644 index f4ec60d961..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - yudao-module-iot-plugin - cn.iocoder.boot - 2.2.0-snapshot - - 4.0.0 - yudao-module-iot-http-plugin - ${project.artifactId} - 2.2.0-snapshot - 物联网 插件模块 - http 插件 - - - - maven-jar-plugin - 2.4 - - - - ${plugin.id} - ${plugin.class} - ${plugin.version} - ${plugin.provider} - ${plugin.description} - ${plugin.dependencies} - - - - - - maven-deploy-plugin - - true - - - - maven-shade-plugin - 3.4.1 - - - package - - shade - - - true - shaded - - - cn.iocoder.yudao.module.iot.HttpPluginSpringbootApplication - - - - - - - - - - - org.pf4j - pf4j-spring - 0.9.0 - provided - - - org.projectlombok - lombok - 1.18.34 - provided - - - - cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin - 0.0.1 - http-plugin - http-plugin-0.0.1 - ahh - - diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties deleted file mode 100644 index 44f221cb15..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties +++ /dev/null @@ -1,6 +0,0 @@ -plugin.id=http-plugin -plugin.class=cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin -plugin.version=0.0.1 -plugin.provider=ahh -plugin.dependencies= -plugin.description=http-plugin-0.0.1 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml deleted file mode 100644 index 4658a1f6bf..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml +++ /dev/null @@ -1,157 +0,0 @@ - - - - yudao-module-iot-plugin - cn.iocoder.boot - ${revision} - - 4.0.0 - jar - - yudao-module-iot-http-plugin - - ${project.artifactId} - - 物联网 插件模块 - http 插件 - - - - - http-plugin - cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin - 0.0.1 - ahh - http-plugin-0.0.1 - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.6 - - - unzip jar file - package - - - - - - - run - - - - - - - maven-assembly-plugin - 2.3 - - - - src/main/assembly/assembly.xml - - - false - - - - make-assembly - package - - attached - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.4 - - - - ${plugin.id} - ${plugin.class} - ${plugin.version} - ${plugin.provider} - ${plugin.description} - ${plugin.dependencies} - - - - - - - maven-deploy-plugin - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - org.springframework.boot - spring-boot-starter - - - - org.pf4j - pf4j-spring - provided - - - - cn.iocoder.boot - yudao-module-iot-api - ${revision} - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - io.vertx - vertx-web - 4.5.11 - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml deleted file mode 100644 index 9056af48a3..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/resources/application.yml +++ /dev/null @@ -1,12 +0,0 @@ -spring: - application: - name: yudao-module-iot-http-plugin - -# MQTT-RPC 配置 -mqtt: - broker: tcp://chaojiniu.top:1883 - username: haohao - password: ahh@123456 - clientId: mqtt-rpc-client-${random.int} - requestTopic: rpc/request - responseTopicPrefix: rpc/response/ diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/assembly/assembly.xml deleted file mode 100644 index daec9e4315..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/assembly/assembly.xml +++ /dev/null @@ -1,31 +0,0 @@ - - plugin - - zip - - false - - - false - runtime - lib - - *:jar:* - - - - - - - target/plugin-classes - classes - - - diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/plugin.properties similarity index 55% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/plugin.properties index a23bafcf79..7f565b75e9 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/plugin.properties +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/plugin.properties @@ -1,6 +1,6 @@ -plugin.id=emqx-plugin +plugin.id=plugin-emqx plugin.class=cn.iocoder.yudao.module.iot.plugin.EmqxPlugin -plugin.version=0.0.1 +plugin.version=1.0.0 plugin.provider=ahh plugin.dependencies= -plugin.description=emqx-plugin-0.0.1 +plugin.description=plugin-emqx-1.0.0 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/pom.xml similarity index 99% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/pom.xml index 43d67f5207..266e45fd34 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/pom.xml @@ -11,7 +11,7 @@ 4.0.0 jar - yudao-module-iot-emqx-plugin + yudao-module-iot-plugin-emqx ${project.artifactId} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java index e64695b06d..27b90426b2 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java @@ -1,12 +1,11 @@ package cn.iocoder.yudao.module.iot.plugin; -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; import org.pf4j.PluginWrapper; -import javax.annotation.Resource; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -14,8 +13,6 @@ import java.util.concurrent.Executors; public class EmqxPlugin extends Plugin { private ExecutorService executorService; - @Resource - private DeviceDataApi deviceDataApi; public EmqxPlugin(PluginWrapper wrapper) { super(wrapper); @@ -30,7 +27,7 @@ public class EmqxPlugin extends Plugin { executorService = Executors.newSingleThreadExecutor(); } - deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); + DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); if (deviceDataApi == null) { log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); return; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml new file mode 100644 index 0000000000..260ef9c8d9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml @@ -0,0 +1,43 @@ + + + + yudao-module-iot-plugin + cn.iocoder.boot + 2.2.0-snapshot + + 4.0.0 + yudao-module-iot-plugin-http + ${project.artifactId} + 1.0.0 + 物联网 插件模块 - http 插件 + + + + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + com.example.HttpPluginSpringbootApplication + + + + + + + + + + ${project.artifactId} + ${project.artifactId}-${project.version} + cn.iocoder.yudao.module.iot.config.HttpVertxPlugin + ${project.version} + yudao + + diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties new file mode 100644 index 0000000000..49aef5b187 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties @@ -0,0 +1,6 @@ +plugin.id=yudao-module-iot-plugin-http +plugin.class=cn.iocoder.yudao.module.iot.config.HttpVertxPlugin +plugin.version=1.0.0 +plugin.provider=yudao +plugin.dependencies= +plugin.description=yudao-module-iot-plugin-http-1.0.0 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml similarity index 73% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml index 3d58a1a75e..40bb303bc8 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-demo-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml @@ -11,45 +11,27 @@ 4.0.0 jar - yudao-module-iot-demo-plugin + yudao-module-iot-plugin-http + 1.0.0 ${project.artifactId} - 物联网 插件模块 - demo 插件 + 物联网 插件模块 - http 插件 - demo-plugin - cn.iocoder.yudao.module.iot.plugin.DemoPlugin - 0.0.1 - ahh + ${project.artifactId} + cn.iocoder.yudao.module.iot.config.HttpVertxPlugin + ${project.version} + yudao + ${project.artifactId}-${project.version} - - + org.apache.maven.plugins maven-antrun-plugin @@ -60,7 +42,8 @@ package - + @@ -92,6 +75,7 @@ + org.apache.maven.plugins maven-jar-plugin @@ -103,12 +87,30 @@ ${plugin.class} ${plugin.version} ${plugin.provider} + ${plugin.description} ${plugin.dependencies} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + -standalone + + + + + maven-deploy-plugin @@ -122,15 +124,12 @@ org.springframework.boot - spring-boot-starter-web - ${spring.boot.version} - provided + spring-boot-starter org.pf4j pf4j-spring - provided @@ -141,8 +140,11 @@ org.projectlombok lombok - ${lombok.version} - provided + + + + io.vertx + vertx-web \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml similarity index 74% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml index daec9e4315..9b79e6152f 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/assembly/assembly.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml @@ -14,14 +14,7 @@ - + target/plugin-classes diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/HttpVertxPlugin.java similarity index 93% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/HttpVertxPlugin.java index 54d9c7c2bc..d77f990c20 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/HttpVertxPlugin.java @@ -1,7 +1,8 @@ -package cn.iocoder.yudao.module.iot.plugin; +package cn.iocoder.yudao.module.iot.config; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.service.HttpVertxHandler; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; @@ -17,8 +18,6 @@ public class HttpVertxPlugin extends SpringPlugin { private static final int PORT = 8092; private Vertx vertx; - private DeviceDataApi deviceDataApi; - public HttpVertxPlugin(PluginWrapper wrapper) { super(wrapper); } @@ -28,7 +27,7 @@ public class HttpVertxPlugin extends SpringPlugin { log.info("HttpVertxPlugin.start()"); // 获取 DeviceDataApi 实例 - deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); if (deviceDataApi == null) { log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); return; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java similarity index 94% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java index b32a1f59fb..1931268b60 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.config; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; -import cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin; import org.pf4j.DefaultPluginManager; import org.pf4j.PluginWrapper; import org.springframework.context.annotation.Bean; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java similarity index 98% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java index 335d6c95d2..8542cfefb1 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.plugin; +package cn.iocoder.yudao.module.iot.service; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml new file mode 100644 index 0000000000..c5a1ee84cf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + application: + name: yudao-module-iot-plugin-http diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/plugin.properties b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/plugin.properties similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/plugin.properties rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/plugin.properties diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/pom.xml similarity index 99% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/pom.xml index 462fbd0901..7e4689b4ae 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/pom.xml @@ -11,7 +11,7 @@ 4.0.0 jar - yudao-module-iot-mqtt-plugin + yudao-module-iot-plugin-mqtt ${project.artifactId} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-emqx-plugin/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-mqtt-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java rename to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java From 916024b8914482eb6b4e32f52e75b4193aa4e108 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 21 Jan 2025 19:38:41 +0800 Subject: [PATCH 091/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E9=AA=8C=E8=AF=81=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E7=8B=AC=E7=AB=8B=E3=80=81=E5=86=85=E5=B5=8C=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 0 -> 10353 bytes .../framework/common/enums/RpcConstants.java | 17 +++++++ yudao-module-iot/yudao-module-iot-api/pom.xml | 8 ++++ .../module/iot/api/device/DeviceDataApi.java | 41 ++++++++++++++-- .../device/dto/DeviceDataCreateReqDTO.java | 31 ------------ .../dto/IotDeviceEventReportReqDTO.java | 45 ++++++++++++++++++ .../dto/IotDevicePropertyReportReqDTO.java | 39 +++++++++++++++ .../dto/IotDeviceStatusUpdateReqDTO.java | 40 ++++++++++++++++ .../yudao/module/iot/enums/ApiConstants.java | 16 +++++++ .../iot/api/device/DeviceDataApiImpl.java | 26 ++++++++-- .../iot/emq/service/EmqxServiceImpl.java | 6 +-- .../plugin/UnifiedConfiguration.java | 6 +-- .../device/IotDevicePropertyDataService.java | 4 +- .../IotDevicePropertyDataServiceImpl.java | 7 +-- .../yudao-module-iot-plugin-http/pom.xml | 2 +- .../iot/HttpPluginSpringbootApplication.java | 17 ++++++- .../module/iot/config/TestConfiguration.java | 44 +++++++++++++++-- .../module/iot/service/HttpVertxHandler.java | 8 ++-- 18 files changed, 294 insertions(+), 63 deletions(-) create mode 100644 plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar create mode 100644 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/RpcConstants.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar new file mode 100644 index 0000000000000000000000000000000000000000..fef8d5362c38ea47e769e58933c715d492df719f GIT binary patch literal 10353 zcmb_?1zeQd(?6YyEF~o!5`uJtpmf91(jhHfvVhb|hvbqX-6co}NOz}nN=t`;@WS$fkzUr?_<<-}A(n55*z zA1eM%h62NRO{QCLl-6u;D&D zD$7WRFfo)XBef=&t|~@Pbt(6cIZ>S!&c1a&@GWs{j6dauNcJ6D!Rt(I`HpZHoVKSo zx`%Bkm?atsM=I_$PnXx>D^n4-_{CZqAlFv%8nc-zty&=#3hzBU0zCr@pX`Rv>UHj? zW3e8~YLVYhZh~=8YijJxwJQ0D;Az)HPot+MeZr|pEHs#|YDQx~|DsU|Y2UnBIPQU- zyQ}(Vo4oCU*^#dHd0K|eLs>H&bC1sQ3~Nu=A1nV^LGafrH+ueuYJMg_|4v|TV`O7& zV*d+5tp6l*b22ut`2{(_-;rC}7&}>+{DSM=FSyKY9Dl({^)H-8HqW1#oBkgJ*jhQ6 zn%`;?<-09XHyB94!N3F~z`*eRcQa5iad3R{+hm;V4IIsFo--R+88|p3gRB*BM1Y>L zcNrwyu&~5LSHzeIX3PQ}ipT*CIp`&$M-G>cYnXz!znIuy?&+;(({k-0pQeYD%lK*m zh?uKqsy{(ZQ{0@toQD_B!3Z=<`@t@wb>OoGP(T7b?8seZ)@Z#zQ*ot61TXU5+4mFL zXRb)Yt`d;8B{1S7*1Wp_so_K3%L!Ua+4GSS&nU{(GlJe@_4J@+hv?RjrM>JJCp2(X zW=KUe?qw%1iRY^9jB48unlm?%%ba@}Nj-FzrH{o0TuDz%X<3~f!IUNjnnW?`5Ti33 zVz2&4F1;dDqPwis;04MmRSjf!#5Er3+J#Vd@i%QBfkkRUV!rw)3jxJ4&h#0$ox=yf9!%SWT8wRCE= z1o_S^jRc$X;OgMqjpRF7UfyC9j<8W0fR|LT5eBTS*!xvdMX=L+MNWyw*mZ7Y=o)um zX3_c;e)xj#1}YjYvktvB&{0Gf7=iys1EpsDZlb*^Pp?fB_Yw(hDi$#|e;yC30?}B6 z*+&ff0g)5~P}+6ZdV-{D(K)-XZ*2E-uve2_8ZE6xG}K-sY+9{>E|yh<%KIeiXeMN$%yG_|HIWZHa;zngYIQqjvD5n6 zEi$T(jak@Q2l7rn*c!hEqTzdYWJZd12~Ua=%OY8(s#k9xyfH4`*+Q2XMh(`TU#Ee5 zmUZU$zvD<$BI}rTK61P$k|PFn?hHseL)#0wzm2hiSd$YozoLy@2Oyy+4#HD9x>wr7 zsgO<<2LQwqw3t9rkURn6nOq0(aDBM)Nx7ZcT_*2`tAZ?%iV~~5DIi5lOyQY_i1#JZ zyS_;l?64i0>Kdav@zdcnSDEOyv{+9Y#23k|7A>0WY30R5_u6YBIg5Rf;V0WnqznNE zO`%Y=V;OXmym-B)#O(0tj3^m1fjz;G!<$ zuF3`TDWs9RH9w~C2%R>PVny+xxd<==zfSq1QwCjv%N2Y>D-uSvXvVM@A-#mak%ik% zasIGqZ?N9quZbe z$y?Da^;H@QtLQ_0Y=BGSgZyK(q2>uuFXA$42`OS)&GHP1b|LRjPwzeNX8Um=r|6k`JY8|TQm0z2bdaj~n& zEQjMMpdTNQ&Kp=k#h9H9{t(u9w zqpPIBb7LzL`|tMNs{Yg#XPodd4E(8L;$>mWOE_3w3i7X&Y1-0O*c<3bzBY|*95mMD zufsAc)N8xGL^euIanMVdVG|`p$7a1hlrDyi6P@sL=JQy~@aU1v#`LhBu0Nu|^jM3& zG^JTeWJv+A_ekyuKVST<9Ymj|-c}k~% z0_m#DvJR3U?0Q?ez>t%p3>+yzBWe!wD@>GclCoD%?V!Ps=;!H7YF$@c(`1Zev;Ou5 z_{rc)P!uu-zqwu9LJ>6?H_nG)p!m}<rMUZNZ8BWu z!HTw6(p(K0KJ?FrgoMmaMdQG>I>3tEecnc3VB3q>yw3_NRrer_v3E*$*wrys%d3=ak ziHL!%P*3m4@B_`fwF8vWjHhJ1+jp>xsC5+BL`FTuoy2`-xh8zS*_UzgZPH;C0WPG& zjU*ZZI2!yCW1%uAX0Txz!;}D_Ez@ol>3T$!SRq@(fQCNTJ0?lVL?tg^@0gQs9?o$X z0cl=~gvR3_S4T#Vro9)Xjcu|uY3mHw93FV+D~BiV{WQ;0wiyectZmCrKT}r`RFu?F zJv5UJfMutrAsgN`7m(*Ln#C2w(XX+eXiOV1uA z`x=WjYf-N-7dEz-rDaNSMY#jAgFh>;mL2L+xl34Aa2N}llr>@Ahj>rjlU4ap<|^%- z-w4lHn@ncPtgo?i#um3>=t}Xz9fNt{3Z)n4!fz=|X|%&Q=h!4!Nn&UBL?zZf_6;_h zJn#AnHmZdOgFKrRk@P`Fz|u1SmYq0dkpezz9g2%idm(6#qZeOU2B`ND9}g_mTz<;- zq*wca3+S6%CyII-24nlV1i@GrWxZchabUmYDVn^}*Yp=X1C`)jokr0&i(+LgQl~jQ za$Lq}ayqTuDkSXM8Oa$z>X~-aM>$j-Y8RXs#S0Gv70#n5nq$oZJIxyh#)ydTEINHi zngDn5z9$)tsE@CF zpWH4p=!J6a>9;h^4a%u05((EP&N4*xI?k9Xf)*NChSS0%{D&ZcUZ~0BQXj#?8chNU z8-%W}UuW;4qCZAwsACcgV~_1-el*Rb9}QAO-CxRlZ4nr7r@0JCjzS{T#hl~ocq=1& zRDXX`Er-MBZ7pok;Z$u$Q9(OEz-f5 zSQo_MXllmCB8~1U#j*D@lbC_&Vag0Jkez{@I<|7ND?y@lM)Pk%g%n>Yr7RJ@6@SDG z*kC$SSFn6JrvDAEHx5j|*w(G~bUNcYH-x|%RB5z|ag9|Y zhr)53V+>(n3Yd4q?SV0@(LSI|%ADGU1fd_PP|^PA9ouzA*u#M{Yl>IGb!u7w@hASZBz_H0J>p**Jkqrz8f zjuBNU!nmj}EC&5e21JJ{mk6cnOiSUjOK=_<;On+NPrBbH!{MNij^QQBe<`~|OQ!#l z!)4KidT(y6(Q80vZ>nA8u$)K$LKa11P1hj5$cUSRmWOxIa%F`8D%oyWQv}wA;YN#v zug7xh)|1lBn`=UYFHoBWq}niscdyV zqg6Md9rvHN0O^u+vfot`N`2bW{CL8v9hhzI7FjQ`?mpR|GhqFuCbVq%G1jXZ!?&uV z+!wH;Zb{H?c4*q0=_IidqUG-XZ%c65VA&)JCp5i=5F4hI2kj5Rb4hhvILC%im9e>gP{! z#|b?#$>F={@bRL}U}P5IHe6*w^k2ip3hP9}8HSn<8=RDc0N4|unzo4(Tyjxa79+E- zClfNpGSZ|*>AK{MhMo>(nV$=MBi4BLXywt`@J`PbbDWN=vX_28)S#M?t= zOic^wrb0+U3V4}9&qqNV))=xa@?@~*4dG%eb}&Q*M9A?(DS0?8T~u2)bJUJfaN0hM z2G3;kS$tZHs^R8inU8>1QeaXwmT0CobB6qRE2oxdihhQuG;C^ycuxi&KZo4wU1KLy zJBODTrBEGIHF!%UOFfS*P3X*ws2*x29e`;rQnck1SC^O0CC))MreEl1Pk3TL@&vDQ zEu1eam+>OrDEA=QZD7z5iM}V9w&-;sW)I?UL4M_oBKzvH@QxCxBaxr_PMngrG-M^M zx{HwX&NRB&{t6N#`&nOu`-wkhmQ700)?#ecmt})q=K_tDnc<~9_F}a@qr4D3PouNr z&VHNZ8h4Y6KL74vvU(vwyMw-t&eIX->1hR227lf5nIrY(P;S4C;6pfNrX^UcP?PC0 z*?bt{g*Zj3^t(;~MothRwzrB;Rx2KNmMGtHp~+=}FcRE2exko06K+=sF5*OxHFjn9 zgm*GEo!FB-MTlGRkhwTXB(Pe|uzWOCY2Y5rfu;@~X7_}@1WE~`f7)~dfnel#x=0f# zG%pM-hcC21kMl8KYjG!_RjW?05nW3g@AWq*wW&UZpq_ZTA1z_;M7LrB;hhydFXi@1 z|JeItOcpYvT6F8U{LdJVu!7l`*0z>dGiSTYzHN}JCwWHhf&++EmSd%o8w2UFhMoPU zv`pSD(eYL+1@OJaFv+}QDL@L4ea zy)?<_uw4^Xt+wOgLN=Mld%P$c9IuSQB(Dg^9!m9=G%favR&QspEk-^B7KQ}LU4U z>99VA1z`jlVFe-XymT2Q|JZ5_Chk`jnwW;RtEC*jpsw=6A`0t14P%imb4_jCzJvo~ z_RE~shv4tREA!1YggW=2INS5*fiyo)4eCc605PkD+}JQ5DmC9-n6fWmyIfrJ(yiq< z*#C4I$bGQ8-xPfMRDDO+>nR;S#S*oAV0@ku)^M5IGv|}1pGzk5gD*Awh8J^rniUh% zD2$Es);pWS1M4elYt!vNUoh_B;4iL(yPaxOcPs^t!8F&PBxC4P$1q7+@_V5c4m)&)aL9bZc*&t)W8&S^hw zB{E^yHPqXv>mcmrLp=Q!a+fkTDGWY&22c>1g^`CvR07DBIHe)ZAqUwfJVEBtbWY zLyUIgd8viMDg{;S=7zqFWZ~VR{P(QW4JD~Poi0IGs_H@B`f4^N9aA_XI8?>O-62{c zvu{y9=HGQ@!$M}O@8lp!NFE7TMXY7P2MJBLFrkulRE1&^a`ixD*&#i!MF`rNEosLz z>DnwjC!6KfcJeH4XdfVE>d3MVN(9)=9qTNjAJN-;Cs0P|+jp~(+efl^oau+k`awy}l z$E_uYGF0+>7!3-l+&!(wM$nMX`o-FsY~GUqO4ss31J>AWK|!&dV7qArIGxE@zw z^;`oC#w%lEp}snqB+a#ln+^)y0rnWP=`(DQx!Slm7Rd&mqd5d&AV~vKuJKoDG$~N$ z25Pj2-zaxU{W0H|mk-`znFY39SJUVus(>qe;+o+wLbUJDgIBKX!M{h zIGWKR=XcO&?cTZL`*>oL09po;{TG7{K(5&FE!^dHYk8JXVeN3d!`iS2^*7NEbQC-% zC6B-Ek>gHoQ$Bj9&weM0JEe{5g?r`jlPdUrtW}f69;AaP6Lsa|iYR??%S@KE@$&FD z;1I)7Sqf?v+cHMPG(Q120dXLy1Z!_Ko|#-w(VP5}z!98pW=)%xHjnuQ!n}obwh2O5 zHH>)FxFM?>vkHh!QG{olIozr+0(VtSy{z~2QODwp8p9h8M!WTH3eGsvOhY&Ac_K=Ivw7g|8p9#9HQ`_w`iOEB5?`9q#}oLcn$*aR^t9k_+mNEv=CO77`Q zKP|t1PTsQNgCPg+xGksq^rqtcZmX{CYcetJj6;vZV%9>2P@836Wf^IFVPNV9L#5C8 z_34$+#N@0hX-Bt);S@4!*?b+aEcnbB-=ZW&1-rLR$&%)im!GF{ZX?@43iWjPRlDl* zRLoQ@mo@~}No12sW|Mcr`q+t-JgxTE=HO7BfrH!4+=zFJ6r7=#m7VPS<=YY^$S(#I zjftIDngr_gW9VROo`SrqM{~aUeSMU=RuvnD+VHZ1ysbo6eKKd8w%$|H6MptjiL0B6 zm5uq9-q7_8#=O4SnAe{Mf4XsQ)>fRMBJ%L80GJ&&f10YSd}U!(dC8;~IVuC5bb2G%$>F@{$CT8#+qD?Gd6Y*> z)6PMbTUj5}vU<^y>o9+QY2jAM*HBf!10ay%##5M^qHgT#NU5h)APGmO_Q*F0dWBtH z`GDZwNXb_!i8z1y_VnpY*YN(U&Id#X=s)cI=c?4r^8cvoSsOT;+!kJbCMNrt*yuU) z?-ecPn@8XmWuKd*_+OCz%KTfYi|Kpqis`xv#bkEV+SQ&C<%95s(DFmh37U(WL*k zNDZJ<5NkVKqy&F$9v{SCh+3v*&Zx@Dz2h8|obM<#zFaLCSQHwog}+Kr+zGK4MT47? z>_Du)_<|_(1cw8a0fRH63WS5ijs~~*k=+^g|ukBk9r~{=r za2~f;&9GRAT5#RZMQW6TVqtlYvt?u9@GNGW7cm%o(EY06 z>g=?A=j!6v5;z)TW6bKzv@tEMq_``m8U+teSo&a3UcWU(OB}t6UEgx$&G3GPpa=2n z9+_%OBHuBoq-DxyQTq3XPLq*R{3zFH(+1c%VT!ui!#!f;lzy&tA%tvPHN4jq20$)~ zZj_|$P_>H5l6S`n6mOLkF zZE+9EHwUX?9vl1Yw8N{$Jf?k3%dZ*Idq!&3W^+a|9)W8G)Ok85QA|BkvT-t2>vbXx zt!P;@VfXK|*sfG*-hTa@~1Lbc~LzMYziYC^DBgj6Rl>zP#P$ zA(~^u^s%BM9+EYb=o8#BdL|T-MPN-Txp`s5@Qr@%MtCkehSMY_X{qD1CT2%}#8D4c zt9U9psIH$lGG;wbWK5-%sG9K|6zh2|z#E6nKK0?L%@i3`f_XuGR}{?<%oN%-_#<1t zSoVqgy{z}(A~rQW9qt9o4=%kj7)&TI!#{775`9+$soavJB}txR?Q#MIMtCGlCL>59 zJJ2ju3A&&7>LkG>`N|5%CTH= zjZ1gxn}V;ZSJU;r-Ogbf0`H%TE~gZk_cl+ut{Q+z{W44Db8b*FSvxpV~ieCH%ap+%!&nA2+}h z*IV*$Z501J)0;+$TZYHq8E)Gu{+aQnvEr7|@;@1Wv|0Q!`%R<8EqlV>vH#I_@y}B3 z{Sfk-o(Fy*<$D{(KTEo4#P~jL6o+;lv|cOjhaSE!=lA*CNcy8Kzt8X=+BR-Y=V7K75M)`6F=ensJ{Od>91wZTO{)9B;>D< zepk`o7d`(9^Vh8AElm7%p7VbL^Zi`%J;(W1l)s*FZc%ox?fX|K|J8}-&v1ViY2U(q zBKj%rzGeQ|OWee%-vS}`cPk!b{)cV5?e`dakTW^^WuOGoboYZ&bzXqben#oNZ zdn@emzYF`9sP@OSzx&p|#cn.iocoder.boot yudao-common + + + + org.springframework + spring-web + provided + + diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java index 6eed3592b5..c2d36e18c6 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java @@ -1,8 +1,17 @@ package cn.iocoder.yudao.module.iot.api.device; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; +import cn.iocoder.yudao.module.iot.enums.ApiConstants; +import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +// TODO 芋艿:名字可能看情况改下 /** * 设备数据 API * @@ -10,11 +19,35 @@ import jakarta.validation.Valid; */ public interface DeviceDataApi { + // TODO @芋艿:可能会调整 + String PREFIX = ApiConstants.PREFIX + "/device-data"; + /** - * 保存设备数据 + * 更新设备状态 * - * @param createDTO 设备数据 + * @param updateReqDTO 更新请求 */ - void saveDeviceData(@Valid DeviceDataCreateReqDTO createDTO); + @PutMapping(PREFIX + "/update-status") + @PermitAll // TODO 芋艿:后续看看怎么优化下 + CommonResult updateDeviceStatus(@Valid @RequestBody IotDeviceStatusUpdateReqDTO updateReqDTO); + + /** + * 上报设备事件数据 + * + * @param reportReqDTO 设备事件 + */ + @PostMapping(PREFIX + "/report-event") + @PermitAll // TODO 芋艿:后续看看怎么优化下 + CommonResult reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO); + + /** + * 上报设备属性数据 + * + * @param reportReqDTO 设备数据 + */ + @PostMapping(PREFIX + "/report-property") + @PermitAll // TODO 芋艿:后续看看怎么优化下 + CommonResult reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java deleted file mode 100644 index 94bc84b804..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/DeviceDataCreateReqDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import jakarta.validation.constraints.NotNull; - -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class DeviceDataCreateReqDTO { - - /** - * 产品标识 - */ - @NotNull(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotNull(message = "设备名称不能为空") - private String deviceName; - /** - * 消息 - */ - @NotNull(message = "消息不能为空") - private String message; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java new file mode 100644 index 0000000000..373905c946 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.api.device.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +/** + * IoT 设备【事件】数据上报 Request DTO + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class IotDeviceEventReportReqDTO { + + // TODO 芋艿:要不要 id + // TODO 芋艿:要不要 time + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + /** + * 事件标识 + */ + @NotEmpty(message = "事件标识不能为空") + private String identifier; + /** + * 事件参数 + */ + @NotEmpty(message = "事件参数不能为空") + private Map params; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java new file mode 100644 index 0000000000..37a4c6c984 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.api.device.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; + +/** + * IoT 设备【属性】数据上报 Request DTO + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class IotDevicePropertyReportReqDTO { + + // TODO 芋艿:要不要 id + // TODO 芋艿:要不要 time + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + /** + * 属性参数 + */ + @NotEmpty(message = "属性参数不能为空") + private Map params; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java new file mode 100644 index 0000000000..0b08f2bd11 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.iot.api.device.dto; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * IoT 设备状态更新 Request DTO + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class IotDeviceStatusUpdateReqDTO { + + // TODO 芋艿:要不要 id + // TODO 芋艿:要不要 time + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + /** + * 设备状态 + */ + @NotEmpty(message = "设备状态不能为空") + @InEnum(IotDeviceStatusEnum.class) // 只使用:在线、离线 + private Integer status; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java new file mode 100644 index 0000000000..2c4147be1f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.enums; + +import cn.iocoder.yudao.framework.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author 芋道源码 + */ +public class ApiConstants { + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/iot"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java index eea7b2a963..cdcfdfdfd6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java @@ -1,16 +1,21 @@ package cn.iocoder.yudao.module.iot.api.device; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; -import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + /** * 设备数据 API 实现类 */ -@Service +@RestController @Validated public class DeviceDataApiImpl implements DeviceDataApi { @@ -18,8 +23,19 @@ public class DeviceDataApiImpl implements DeviceDataApi { private IotDevicePropertyDataService deviceDataService; @Override - public void saveDeviceData(DeviceDataCreateReqDTO createDTO) { - deviceDataService.saveDeviceData(createDTO); + public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { + return success(true); + } + + @Override + public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + return success(true); + } + + @Override + public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + deviceDataService.saveDeviceData(reportReqDTO); + return success(true); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 3c21a55ca8..222d1d50af 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.emq.service; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -34,10 +34,10 @@ public class EmqxServiceImpl implements EmqxService { String productKey = topic.split("/")[2]; String deviceName = topic.split("/")[3]; String message = new String(mqttMessage.getPayload()); - DeviceDataCreateReqDTO createDTO = DeviceDataCreateReqDTO.builder() + IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() .productKey(productKey) .deviceName(deviceName) - .message(message) +// .properties(message) // TODO 芋艿:临时去掉,看看 .build(); iotDeviceDataService.saveDeviceData(createDTO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java index 150051ce58..e27d9b5fb3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -7,8 +7,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.nio.file.Paths; - // TODO @芋艿:需要 review 下 @Slf4j @Configuration @@ -21,8 +19,8 @@ public class UnifiedConfiguration { // @DependsOn("deviceDataApiImpl") public SpringPluginManager pluginManager() { log.info("[init][实例化 SpringPluginManager]"); - SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { -// SpringPluginManager springPluginManager = new SpringPluginManager() { +// SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { + SpringPluginManager springPluginManager = new SpringPluginManager() { @Override public void startPlugins() { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index a882b5d6cb..396cbf79fd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import jakarta.validation.Valid; @@ -28,7 +28,7 @@ public interface IotDevicePropertyDataService { * * @param createDTO 设备数据 */ - void saveDeviceData(DeviceDataCreateReqDTO createDTO); + void saveDeviceData(IotDevicePropertyReportReqDTO createDTO); /** * 获得设备属性最新数据 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 0f9523414e..aefaac6960 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -6,7 +6,7 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -130,11 +130,12 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe } @Override - public void saveDeviceData(DeviceDataCreateReqDTO createDTO) { + public void saveDeviceData(IotDevicePropertyReportReqDTO createDTO) { + // TODO 芋艿:这块需要实现 // 1. 根据产品 key 和设备名称,获得设备信息 IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName()); // 2. 解析消息,保存数据 - JSONObject jsonObject = new JSONObject(createDTO.getMessage()); + JSONObject jsonObject = new JSONObject(createDTO.getParams()); log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject); ThingModelMessage thingModelMessage = ThingModelMessage.builder() .id(jsonObject.getStr("id")) diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml index 40bb303bc8..cfea78964c 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml @@ -124,7 +124,7 @@ org.springframework.boot - spring-boot-starter + spring-boot-starter-web diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java index 2b871cadea..7b29367d21 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java @@ -1,13 +1,26 @@ package cn.iocoder.yudao.module.iot; import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; +// TODO @haohao:建议包名:cn.iocoder.yudao.module.iot.plugin.${pluginName},例如说 http。然后子包如下: +// config:方配置类,以及 HttpVertxPlugin 初始化 +// service:放 HttpVertxHandler 逻辑; @SpringBootApplication public class HttpPluginSpringbootApplication { public static void main(String[] args) { - SpringApplication.run(HttpPluginSpringbootApplication.class, args); +// SpringApplication.run(HttpPluginSpringbootApplication.class, args); + SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.run(args); } -} \ No newline at end of file +} + +// TODO @haohao:如下是 sdk 的包:cn.iocoder.yudao.module.iot.plugin.sdk +// 1. api 包:实现 DeviceDataApi 接口,通过 resttemplate 调用 +// 2. config 包:初始化 DeviceDataApi 等等 + +// 3. 其中 resttemplate 调用的后端地址,通过每个服务的 application.yaml 进行注入。 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java index 1931268b60..18f6b285ef 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java @@ -1,23 +1,59 @@ package cn.iocoder.yudao.module.iot.config; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; import org.pf4j.DefaultPluginManager; import org.pf4j.PluginWrapper; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; // TODO 芋艿:临时实现; @Configuration public class TestConfiguration { +// @Resource +// private RestTemplate restTemplate; + + // TODO 芋艿:这里,后续看看怎么创建好点 @Bean - public DeviceDataApi deviceDataApi() { + public RestTemplate restTemplate() { + return new RestTemplateBuilder().build(); + } + + @Bean + public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { return new DeviceDataApi() { @Override - public void saveDeviceData(DeviceDataCreateReqDTO createDTO) { - System.out.println("saveDeviceData"); + public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { + // TODO haohao:待实现 + return null; + } + + @Override + public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + // TODO haohao:待实现 + return null; + } + + @Override + public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + // TODO haohao:待完整实现 + String url = "http://127.0.0.1:48080/rpc-api/iot/device-data/report-property"; + try { + restTemplate.postForObject(url, reportReqDTO, CommonResult.class); + return success(true); + } catch (Exception e) { + e.printStackTrace(); + return CommonResult.error(400, "error"); + } } }; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java index 8542cfefb1..b92b0869b5 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.service; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import io.vertx.core.Handler; import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.RoutingContext; @@ -46,12 +46,12 @@ public class HttpVertxHandler implements Handler { try { // 调用主程序的接口保存数据 - DeviceDataCreateReqDTO createDTO = DeviceDataCreateReqDTO.builder() + IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() .productKey(productKey) .deviceName(deviceName) - .message(jsonData.toString()) + .params(jsonData) // TODO 芋艿:这块要优化 .build(); - deviceDataApi.saveDeviceData(createDTO); + deviceDataApi.reportDevicePropertyData(createDTO); // 构造成功响应内容 JSONObject successRes = createResponseJson( From 0707792755e5e416d13664f4c34d302b8f2be154 Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Wed, 22 Jan 2025 16:59:00 +0800 Subject: [PATCH 092/309] =?UTF-8?q?[fix]=EF=BC=9Acode=20=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deviceconsumer/DeviceConsumer.java | 37 +++++++++++++++++++ .../simulatesend/SimulateSendConsumer.java | 1 - .../module/iot/mq/message/package-info.java | 4 ++ .../simulatesend/SimulateSendProducer.java | 9 +++++ .../device/IotDeviceLogDataService.java | 4 ++ .../device/IotDeviceLogDataServiceImpl.java | 6 +++ .../device/IotDevicePropertyDataService.java | 9 +++++ .../IotDevicePropertyDataServiceImpl.java | 9 +++++ 8 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java new file mode 100644 index 0000000000..63e0fed7fd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.deviceconsumer; + + +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; +import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 针对 {@link ThingModelMessage} 的消费者 + * + * @author alwayssuper + */ +@Component +@Slf4j +public class DeviceConsumer { + @Resource + private IotDeviceLogDataService iotDeviceLogDataService; + @Resource + private IotDevicePropertyDataService deviceDataService; + + // TODO @芋艿:这块先用ThingModelMessage,后续看看用啥替代 + @EventListener + @Async + public void onMessage(ThingModelMessage message) { + log.info("[onMessage][消息内容({})]", message); + deviceDataService.saveDeviceDataTest(message); + iotDeviceLogDataService.saveDeviceLog(message); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java index 111cf50073..1003fe7f4c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java @@ -4,7 +4,6 @@ package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; * TODO @alwayssuper:记得实现,还有类注释哈 * * @author alwayssuper - * @since 2024/12/20 8:04 */ public class SimulateSendConsumer { } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java new file mode 100644 index 0000000000..c3adf7c061 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java @@ -0,0 +1,4 @@ +/** + * 消息队列的消息 + */ +package cn.iocoder.yudao.module.iot.mq.message; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java index 411ddd6f2b..1fb0c80c00 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.mq.producer.simulatesend; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; @@ -19,4 +20,12 @@ public class SimulateSendProducer { @Resource private ApplicationContext applicationContext; + /** + * 发送 {@link ThingModelMessage} 消息 + * + * @param thingModelMessage 物模型消息 + */ + public void sendSimulateMessage(ThingModelMessage thingModelMessage) { + applicationContext.publishEvent(thingModelMessage); + } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java index a92d46bf49..2ddf1113aa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; /** * IoT 设备日志数据 Service 接口 @@ -36,4 +37,7 @@ public interface IotDeviceLogDataService { */ PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO); + + void saveDeviceLog(ThingModelMessage msg); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index 85f950d5dd..e514d20491 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -76,4 +77,9 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ return new PageResult<>(list, total); } + @Override + public void saveDeviceLog(ThingModelMessage msg) { + + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 396cbf79fd..63cb5d6d93 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import jakarta.validation.Valid; import java.util.List; @@ -30,6 +31,14 @@ public interface IotDevicePropertyDataService { */ void saveDeviceData(IotDevicePropertyReportReqDTO createDTO); + + /** + * 保存设备数据 + * + * @param thingModelMessage 设备数据 + */ + void saveDeviceDataTest(ThingModelMessage thingModelMessage); + /** * 获得设备属性最新数据 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index aefaac6960..c89f27346f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -150,6 +150,15 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } + //TODO:后续捋一捋这块逻辑,先借鉴一下目前的代码 + @Override + public void saveDeviceDataTest(ThingModelMessage thingModelMessage) { + // 1. 根据产品 key 和设备名称,获得设备信息 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(thingModelMessage.getProductKey(), thingModelMessage.getDeviceName()); + // 2. 保存数据 + thingModelMessageService.saveThingModelMessage(device, thingModelMessage); + } + @Override public List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { List list = new ArrayList<>(); From 2b27085ec2284b0db8ad42da553c0678f08ce526 Mon Sep 17 00:00:00 2001 From: alwayssuper <12851801+alwayssuper@user.noreply.gitee.com> Date: Wed, 22 Jan 2025 22:08:10 +0800 Subject: [PATCH 093/309] feat:simulator2 --- .../admin/device/IotDeviceDataController.java | 6 +++-- .../device/IotDevicePropertyDataService.java | 9 +++++++ .../IotDevicePropertyDataServiceImpl.java | 26 +++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index be451f17a0..7879698e3a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; @@ -54,10 +55,11 @@ public class IotDeviceDataController { // TODO:数据权限 @PostMapping("/simulator") @Operation(summary = "模拟设备") - public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { + public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulcatorReqVO) { //TODO:先生成一下设备日志 后续完善模拟设备代码逻辑 // TODO @super:应该 deviceDataService 里面有个 simulatorDevice,然后里面去 insert 日志! - iotDeviceLogDataService.createDeviceLog(simulatorReqVO); + IotDevicePropertyReportReqDTO simulatorReqVO = new IotDevicePropertyReportReqDTO(); + deviceDataService.simulateSend(simulatorReqVO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 63cb5d6d93..4e535c9f26 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import jakarta.validation.Valid; @@ -39,6 +40,14 @@ public interface IotDevicePropertyDataService { */ void saveDeviceDataTest(ThingModelMessage thingModelMessage); + /** + * 模拟设备 + * + * @param simulatorReqVO 设备数据 + */ + + void simulateSend(IotDevicePropertyReportReqDTO simulatorReqVO); + /** * 获得设备属性最新数据 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index c89f27346f..00b29f07e6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -8,6 +8,7 @@ import cn.hutool.json.JSONObject; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; @@ -22,6 +23,7 @@ import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.mq.producer.simulatesend.SimulateSendProducer; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; @@ -76,6 +78,9 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe @Resource private IotProductService productService; + @Resource + private SimulateSendProducer simulateSendProducer; + @Resource private TdEngineDMLMapper tdEngineDMLMapper; @@ -159,6 +164,27 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } + //TODO: copy了saveDeviceData的逻辑,后续看看这块怎么优化 + @Override + public void simulateSend(IotDevicePropertyReportReqDTO simulatorReqVO) { + // 1. 根据产品 key 和设备名称,获得设备信息 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(simulatorReqVO.getProductKey(), simulatorReqVO.getDeviceName()); + // 2. 解析消息,保存数据 + JSONObject jsonObject = new JSONObject(simulatorReqVO.getParams()); + log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", simulatorReqVO.getProductKey(), simulatorReqVO.getDeviceName(), jsonObject); + ThingModelMessage thingModelMessage = ThingModelMessage.builder() + .id(jsonObject.getStr("id")) + .sys(jsonObject.get("sys")) + .method(jsonObject.getStr("method")) + .params(jsonObject.get("params")) + .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) + .productKey(simulatorReqVO.getProductKey()) + .deviceName(simulatorReqVO.getDeviceName()) + .deviceKey(device.getDeviceKey()) + .build(); + simulateSendProducer.sendSimulateMessage(thingModelMessage); + } + @Override public List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { List list = new ArrayList<>(); From 4f962bd1f73fed4b7fb1c4186300ecd3ca0ac59f Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Thu, 23 Jan 2025 16:54:45 +0800 Subject: [PATCH 094/309] =?UTF-8?q?[fix]=EF=BC=9Acode=20=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 1 + .../admin/device/IotDeviceDataController.java | 7 ++-- .../IotDeviceDataSimulatorSaveReqVO.java | 1 - .../deviceconsumer/DeviceConsumer.java | 4 +- .../simulatesend/SimulateSendConsumer.java | 9 ----- .../device/IotDeviceLogDataService.java | 6 ++- .../device/IotDeviceLogDataServiceImpl.java | 14 +++++++ .../device/IotDevicePropertyDataService.java | 2 +- .../IotDevicePropertyDataServiceImpl.java | 39 ++++++++++++------- .../iot/service/device/IotDeviceService.java | 9 +++++ .../service/device/IotDeviceServiceImpl.java | 5 +++ .../mapper/device/IotDeviceLogDataMapper.xml | 2 +- 12 files changed, 68 insertions(+), 31 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 4539f12591..305aa6c7fd 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -30,6 +30,7 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); + ErrorCode DEVICE_DATA_CONTENT_JSON_PARSE_ERROR = new ErrorCode(1_050_003_007, "导入设备数据格式错误!"); // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 7879698e3a..f78a96b248 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -55,11 +55,10 @@ public class IotDeviceDataController { // TODO:数据权限 @PostMapping("/simulator") @Operation(summary = "模拟设备") - public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulcatorReqVO) { - //TODO:先生成一下设备日志 后续完善模拟设备代码逻辑 + public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { + //TODO:先使用 IotDeviceDataSimulatorSaveReqVO 另外content里数据类型的效验前端也没做,后端应该要要效验一下,这块后续看看怎么安排 // TODO @super:应该 deviceDataService 里面有个 simulatorDevice,然后里面去 insert 日志! - IotDevicePropertyReportReqDTO simulatorReqVO = new IotDevicePropertyReportReqDTO(); - deviceDataService.simulateSend(simulatorReqVO); + deviceDataService.simulatorSend(simulatorReqVO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java index 5e2b085437..4d09808b14 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java @@ -38,7 +38,6 @@ public class IotDeviceDataSimulatorSaveReqVO { @NotEmpty(message = "数据内容不能为空") private String content; - // TODO @芋艿:需要讨论下,reportTime 到底以那个为准! @Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED) private Long reportTime; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java index 63e0fed7fd..862fb4d826 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java @@ -30,7 +30,9 @@ public class DeviceConsumer { @Async public void onMessage(ThingModelMessage message) { log.info("[onMessage][消息内容({})]", message); - deviceDataService.saveDeviceDataTest(message); + // 设备数据记录 +// deviceDataService.saveDeviceDataTest(message); + // 设备日志记录 iotDeviceLogDataService.saveDeviceLog(message); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java deleted file mode 100644 index 1003fe7f4c..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/simulatesend/SimulateSendConsumer.java +++ /dev/null @@ -1,9 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.simulatesend; - -/** - * TODO @alwayssuper:记得实现,还有类注释哈 - * - * @author alwayssuper - */ -public class SimulateSendConsumer { -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java index 2ddf1113aa..2d7bf98500 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java @@ -37,7 +37,11 @@ public interface IotDeviceLogDataService { */ PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO); - + /** + * 插入设备日志 + * + * @param msg 设备数据 + */ void saveDeviceLog(ThingModelMessage msg); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index e514d20491..bee966e72c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.device; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; @@ -12,6 +13,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.Date; import java.util.List; /** @@ -79,7 +81,19 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ @Override public void saveDeviceLog(ThingModelMessage msg) { + // 1. 构建设备日志对象 + IotDeviceLogDO iotDeviceLogDO = IotDeviceLogDO.builder() + .id(msg.getId()) // 消息ID + .deviceKey(msg.getDeviceKey()) // 设备标识 + .productKey(msg.getProductKey()) // 产品标识 + .type(msg.getMethod()) // 消息类型,使用method作为类型 + .subType("property") // TODO:这块先写死,后续优化 + .content(JSONUtil.toJsonStr(msg)) // TODO:后续优化 + .reportTime(msg.getTime()) // 上报时间 + .build(); + // 2. 插入设备日志 + iotDeviceLogDataMapper.insert(iotDeviceLogDO); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 4e535c9f26..92a64c28f8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -46,7 +46,7 @@ public interface IotDevicePropertyDataService { * @param simulatorReqVO 设备数据 */ - void simulateSend(IotDevicePropertyReportReqDTO simulatorReqVO); + void simulatorSend(IotDeviceDataSimulatorSaveReqVO simulatorReqVO); /** * 获得设备属性最新数据 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 00b29f07e6..134803c8e9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; @@ -39,8 +41,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_CONTENT_JSON_PARSE_ERROR; /** * IoT 设备【属性】数据 Service 实现类 @@ -164,24 +168,33 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } - //TODO: copy了saveDeviceData的逻辑,后续看看这块怎么优化 + //TODO: copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 @Override - public void simulateSend(IotDevicePropertyReportReqDTO simulatorReqVO) { - // 1. 根据产品 key 和设备名称,获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(simulatorReqVO.getProductKey(), simulatorReqVO.getDeviceName()); - // 2. 解析消息,保存数据 - JSONObject jsonObject = new JSONObject(simulatorReqVO.getParams()); - log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", simulatorReqVO.getProductKey(), simulatorReqVO.getDeviceName(), jsonObject); + public void simulatorSend(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { + // 1. 根据设备 key ,获得设备信息 + IotDeviceDO device = deviceService.getDeviceByDeviceKey(simulatorReqVO.getDeviceKey()); + + // 2. 解析 content 为 JSON 对象 + JSONObject contentJson; + try { + contentJson = JSONUtil.parseObj(simulatorReqVO.getContent()); + } catch (Exception e) { + throw exception(DEVICE_DATA_CONTENT_JSON_PARSE_ERROR); + } + + // 3. 构建物模型消息 ThingModelMessage thingModelMessage = ThingModelMessage.builder() - .id(jsonObject.getStr("id")) - .sys(jsonObject.get("sys")) - .method(jsonObject.getStr("method")) - .params(jsonObject.get("params")) - .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) + .id(IdUtil.fastSimpleUUID()) // TODO:后续优化 + .sys(null)// TODO:这块先写死,后续优化 + .method("thing.event.property.post") // TODO:这块先写死,后续优化 + .params(contentJson) // 将 content 作为 params + .time(simulatorReqVO.getReportTime()) // 使用上报时间 .productKey(simulatorReqVO.getProductKey()) - .deviceName(simulatorReqVO.getDeviceName()) + .deviceName(device.getDeviceName()) .deviceKey(device.getDeviceKey()) .build(); + + // 4. 发送模拟消息 simulateSendProducer.sendSimulateMessage(thingModelMessage); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index b8a3511cdd..adf3304d02 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -72,6 +72,15 @@ public interface IotDeviceService { */ IotDeviceDO getDevice(Long id); + + /** + * 根据设备 key 获得设备 + * + * @param deviceKey 编号 + * @return IoT 设备 + */ + IotDeviceDO getDeviceByDeviceKey(String deviceKey); + /** * ��得设备分页 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 1896b98b7d..6a28b27f34 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -197,6 +197,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectById(id); } + @Override + public IotDeviceDO getDeviceByDeviceKey(String deviceKey) { + return deviceMapper.selectByDeviceKey(deviceKey); + } + @Override public PageResult getDevicePage(IotDevicePageReqVO pageReqVO) { return deviceMapper.selectPage(pageReqVO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 4a60f8f78d..1e9b3fdfff 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -27,7 +27,7 @@ - INSERT INTO device_log_${log.deviceKey} (id, product_key, type, subType, content, report_time) + INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time) USING device_log TAGS ('${log.deviceKey}') VALUES ( From 03d4f60e8027b2e6125e2db4077d87c72ba72ccf Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Fri, 24 Jan 2025 15:56:01 +0800 Subject: [PATCH 095/309] =?UTF-8?q?[fix]=EF=BC=9Acode=20=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dataobject/tdengine/SelectVisualDO.java | 3 ++ .../dal/dataobject/tdengine/TdTableDO.java | 4 ++ .../dal/tdengine/IotDeviceLogDataMapper.java | 11 ++++- .../tdengine/IotDevicePropertyDataMapper.java | 43 +++++++++++++++++++ .../deviceconsumer/DeviceConsumer.java | 4 +- .../device/IotDeviceLogDataServiceImpl.java | 8 +++- .../IotDevicePropertyDataServiceImpl.java | 5 ++- .../IotThingModelMessageServiceImpl.java | 23 +++++++--- .../mapper/device/IotDeviceLogDataMapper.xml | 8 +++- .../device/IotDevicePropertyDataMapper.xml | 42 ++++++++++++++++++ 10 files changed, 138 insertions(+), 13 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java index 584562bfa6..c9ac20af22 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectVisualDO.java @@ -19,6 +19,9 @@ public class SelectVisualDO { */ private String tableName; + + private String deviceKey; + /** * 属性 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java index 3265c3ebed..104be26837 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java @@ -33,6 +33,10 @@ public class TdTableDO { */ private String tableName; + private String productKey; + + private String deviceKey; + /** * COLUMN 字段 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index 40516c7944..2d8230f1f6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -68,6 +68,15 @@ public interface IotDeviceLogDataMapper { /** * 查询设备日志表是否存在 * + * @return 不存在返回null */ - Object checkDeviceLogTableExists(); + Object checkDeviceLogSTableExists(); + + /** + * 检查设备日志子表是否存在 + * + * @param deviceKey 设备标识 + * @return 不存在返回null + */ + Object checkDeviceLogTableExists(@Param("deviceKey") String deviceKey); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index 6053444fea..1599ce957c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; @@ -10,6 +13,7 @@ import org.apache.ibatis.annotations.Param; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @Mapper @@ -74,4 +78,43 @@ public interface IotDevicePropertyDataMapper { void alterProductPropertySTableDropField(@Param("productKey") String productKey, @Param("field") TDengineTableField field); + //TODO:先参考一下老逻辑,后续改进 + /** + * 插入数据 - 指定列插入数据 + * + * @param table 数据 + * productKey 产品 key + * deviceKey 设备 key + * columns 列 + */ + void insertDevicePropertyData(TdTableDO table); + + //TODO:先参考一下老逻辑,后续改进 + /** + * 查看超级表 - 获取超级表的结构信息 + * SQL:DESCRIBE [db_name.]stb_name; + * + * @param superTable 超级表信息 + * productKey 产品 key + */ + List> describeSuperTable(TdTableDO superTable); + + /** + * 获取历史数据列表 + * + * @param selectVisualDO 查询条件 + * @return 历史数据列表 + */ + @TenantIgnore + List> selectHistoryDataList(SelectVisualDO selectVisualDO); + + /** + * 获取历史数据条数 + * + * @param selectVisualDO 查询条件 + * @return 数据条数 + */ + @TenantIgnore + Long selectHistoryCount(SelectVisualDO selectVisualDO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java index 862fb4d826..1daa05f920 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java @@ -30,8 +30,10 @@ public class DeviceConsumer { @Async public void onMessage(ThingModelMessage message) { log.info("[onMessage][消息内容({})]", message); + //TODO:数据插入这块整体写的比较混乱,整体借鉴了浩浩哥之前写的逻辑,目前是通过模拟设备科插入数据了,但之前的逻辑有大量弃用的部分,后续看看怎么完善 + // 设备数据记录 -// deviceDataService.saveDeviceDataTest(message); + deviceDataService.saveDeviceDataTest(message); // 设备日志记录 iotDeviceLogDataService.saveDeviceLog(message); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index bee966e72c..f66bcc8ad2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -43,8 +43,8 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ // } // throw e; // } - if(iotDeviceLogDataMapper.checkDeviceLogTableExists()==null){ - log.info("[TDengine] 设备日志超级表不存在,开始创建 {}",iotDeviceLogDataMapper.checkDeviceLogTableExists()); + if(iotDeviceLogDataMapper.checkDeviceLogSTableExists()==null){ + log.info("[TDengine] 设备日志超级表不存在,开始创建"); iotDeviceLogDataMapper.createDeviceLogSTable(); }else{ log.info("[TDengine] 设备日志超级表已存在,跳过创建"); @@ -72,6 +72,10 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ // 讨论:艿菇 这就是iotDeviceLogDataService的Impl @Override public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { + // 当设备日志表未创建时,查询会出现报错 + if(iotDeviceLogDataMapper.checkDeviceLogTableExists(pageReqVO.getDeviceKey())==null){ + return null; + } // 查询数据 List list = iotDeviceLogDataMapper.selectPage(pageReqVO); Long total = iotDeviceLogDataMapper.selectCount(pageReqVO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 134803c8e9..2b998e7886 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -244,6 +244,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe SelectVisualDO selectVisualDO = new SelectVisualDO(); selectVisualDO.setDataBaseName(getDatabaseName()); selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); + selectVisualDO.setDeviceKey(device.getDeviceKey()); selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier()); selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); @@ -251,8 +252,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe params.put("rows", deviceDataReqVO.getPageSize()); params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); selectVisualDO.setParams(params); - pageResult.setList(tdEngineDMLMapper.selectHistoryDataList(selectVisualDO)); - pageResult.setTotal(tdEngineDMLMapper.selectHistoryCount(selectVisualDO)); + pageResult.setList(devicePropertyDataMapper.selectHistoryDataList(selectVisualDO)); + pageResult.setTotal(devicePropertyDataMapper.selectHistoryCount(selectVisualDO)); return pageResult; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java index e094b34cb6..c35bb3a28b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; @@ -61,6 +62,9 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ @Resource private TdEngineDMLMapper tdEngineDMLMapper; + @Resource + private IotDevicePropertyDataMapper iotDevicePropertyDataMapper; + @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @@ -71,7 +75,7 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { // 1.1 创建设备表 - createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); +// createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); } @@ -85,14 +89,20 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 3. 过滤并收集有效的属性字段,缓存设备属性 List schemaFieldValues = filterAndCollectValidFields(params, thingModelList, device, thingModelMessage.getTime()); - if (schemaFieldValues.size() == 1) { // 仅有时间字段,无需保存 + if (schemaFieldValues.size() == 0) { // 没有字段,无需保存 return; } // 4. 构建并保存设备属性数据 - tdEngineDMLMapper.insertData(TdTableDO.builder() - .dataBaseName(getDatabaseName()) - .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) +// tdEngineDMLMapper.insertData(TdTableDO.builder() +// .dataBaseName(getDatabaseName()) +// .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) +// .columns(schemaFieldValues) +// .build()); + // TODO:复用了旧逻辑,先过渡一下 + iotDevicePropertyDataMapper.insertDevicePropertyData(TdTableDO.builder() + .productKey(device.getProductKey()) + .deviceKey(device.getDeviceKey()) .columns(schemaFieldValues) .build()); } @@ -145,7 +155,8 @@ public class IotThingModelMessageServiceImpl implements IotThingModelMessageServ // 3. 过滤并收集有效的属性字段 List schemaFieldValues = new ArrayList<>(); - schemaFieldValues.add(new TdFieldDO(TIME, time)); + //TODO:新版本是使用ts字段 +// schemaFieldValues.add(new TdFieldDO(TIME, time)); params.forEach((key, val) -> { if (propertyIdentifiers.contains(key)) { schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 1e9b3fdfff..828522ed70 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -76,8 +76,14 @@ - SHOW STABLES LIKE 'device_log' + + + + \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml index 9894e27a0e..3ac238d751 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml @@ -42,4 +42,46 @@ DROP COLUMN ${field.field} + + INSERT INTO device_property_${deviceKey} + USING product_property_${productKey} + TAGS ('${deviceKey}') + (ts + + ,${item.fieldName} + + ) + VALUES + (NOW + + ,#{item.fieldValue} + + ) + + + + + + + + + + + \ No newline at end of file From 698cec92bdf4f5c416874fd89fb6cbc50b8d9c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Fri, 24 Jan 2025 23:17:26 +0800 Subject: [PATCH 096/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E9=87=8D=E5=91=BD=E5=90=8D=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E6=A8=A1=E5=9D=97=EF=BC=8C=E9=87=8D=E6=9E=84=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E7=AE=A1=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=96=B0=E6=8F=92=E4=BB=B6=E6=9E=B6=E6=9E=84=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/pom.xml | 2 +- .../dependency-reduced-pom.xml | 43 ------------ .../iot/HttpPluginSpringbootApplication.java | 26 ------- .../module/iot/config/TestConfiguration.java | 69 ------------------- .../pom.xml | 3 +- .../yudao-module-iot-plugin-common/pom.xml | 55 +++++++++++++++ .../common/api/DeviceDataApiClient.java | 60 ++++++++++++++++ .../config/DeviceDataApiInitializer.java | 29 ++++++++ .../iot/plugin/common/package-info.java | 1 + .../plugin.properties | 0 .../yudao-module-iot-plugin-emqx/pom.xml | 2 +- .../src/main/assembly/assembly.xml | 0 .../yudao/module/iot/plugin/EmqxPlugin.java | 0 .../plugin.properties | 2 +- .../yudao-module-iot-plugin-http/pom.xml | 20 +----- .../src/main/assembly/assembly.xml | 0 .../http/HttpPluginSpringbootApplication.java | 16 +++++ .../plugin/http}/config/HttpVertxPlugin.java | 4 +- .../config/HttpVertxPluginConfiguration.java | 16 +++++ .../http}/service/HttpVertxHandler.java | 2 +- .../src/main/resources/application.yml | 4 ++ .../plugin.properties | 0 .../yudao-module-iot-plugin-mqtt/pom.xml | 2 +- .../src/main/assembly/assembly.xml | 0 .../yudao/module/iot/plugin/MqttPlugin.java | 0 .../iot/plugin/MqttServerExtension.java | 0 26 files changed, 193 insertions(+), 163 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/pom.xml (87%) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-emqx/plugin.properties (100%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-emqx/pom.xml (99%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml (100%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java (100%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-http/plugin.properties (67%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-http/pom.xml (87%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml (100%) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java rename yudao-module-iot/{yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot => yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http}/config/HttpVertxPlugin.java (95%) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java rename yudao-module-iot/{yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot => yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http}/service/HttpVertxHandler.java (98%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-http/src/main/resources/application.yml (53%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-mqtt/plugin.properties (100%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-mqtt/pom.xml (99%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml (100%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java (100%) rename yudao-module-iot/{yudao-module-iot-plugin => yudao-module-iot-plugins}/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java (100%) diff --git a/yudao-module-iot/pom.xml b/yudao-module-iot/pom.xml index d9002abea5..0422c5d6c8 100644 --- a/yudao-module-iot/pom.xml +++ b/yudao-module-iot/pom.xml @@ -10,7 +10,7 @@ yudao-module-iot-api yudao-module-iot-biz - yudao-module-iot-plugin + yudao-module-iot-plugins 4.0.0 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml deleted file mode 100644 index 260ef9c8d9..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/dependency-reduced-pom.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - yudao-module-iot-plugin - cn.iocoder.boot - 2.2.0-snapshot - - 4.0.0 - yudao-module-iot-plugin-http - ${project.artifactId} - 1.0.0 - 物联网 插件模块 - http 插件 - - - - maven-shade-plugin - 3.2.4 - - - package - - shade - - - - - com.example.HttpPluginSpringbootApplication - - - - - - - - - - ${project.artifactId} - ${project.artifactId}-${project.version} - cn.iocoder.yudao.module.iot.config.HttpVertxPlugin - ${project.version} - yudao - - diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java deleted file mode 100644 index 7b29367d21..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/HttpPluginSpringbootApplication.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.iot; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.WebApplicationType; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -// TODO @haohao:建议包名:cn.iocoder.yudao.module.iot.plugin.${pluginName},例如说 http。然后子包如下: -// config:方配置类,以及 HttpVertxPlugin 初始化 -// service:放 HttpVertxHandler 逻辑; -@SpringBootApplication -public class HttpPluginSpringbootApplication { - - public static void main(String[] args) { -// SpringApplication.run(HttpPluginSpringbootApplication.class, args); - SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); - application.setWebApplicationType(WebApplicationType.NONE); - application.run(args); - } - -} - -// TODO @haohao:如下是 sdk 的包:cn.iocoder.yudao.module.iot.plugin.sdk -// 1. api 包:实现 DeviceDataApi 接口,通过 resttemplate 调用 -// 2. config 包:初始化 DeviceDataApi 等等 - -// 3. 其中 resttemplate 调用的后端地址,通过每个服务的 application.yaml 进行注入。 \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java b/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java deleted file mode 100644 index 18f6b285ef..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/TestConfiguration.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.iot.config; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; -import org.pf4j.DefaultPluginManager; -import org.pf4j.PluginWrapper; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -// TODO 芋艿:临时实现; -@Configuration -public class TestConfiguration { - -// @Resource -// private RestTemplate restTemplate; - - // TODO 芋艿:这里,后续看看怎么创建好点 - @Bean - public RestTemplate restTemplate() { - return new RestTemplateBuilder().build(); - } - - @Bean - public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { - return new DeviceDataApi() { - - @Override - public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { - // TODO haohao:待实现 - return null; - } - - @Override - public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { - // TODO haohao:待实现 - return null; - } - - @Override - public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - // TODO haohao:待完整实现 - String url = "http://127.0.0.1:48080/rpc-api/iot/device-data/report-property"; - try { - restTemplate.postForObject(url, reportReqDTO, CommonResult.class); - return success(true); - } catch (Exception e) { - e.printStackTrace(); - return CommonResult.error(400, "error"); - } - } - - }; - } - - // TODO @haohao:可能要看下,有没更好的方式 - @Bean(initMethod = "start") - public HttpVertxPlugin HttpVertxPlugin() { - PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null); - return new HttpVertxPlugin(pluginWrapper); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/pom.xml similarity index 87% rename from yudao-module-iot/yudao-module-iot-plugin/pom.xml rename to yudao-module-iot/yudao-module-iot-plugins/pom.xml index 949ff13e6f..d33292527b 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/pom.xml @@ -8,6 +8,7 @@ ${revision} + yudao-module-iot-plugin-common yudao-module-iot-plugin-http yudao-module-iot-plugin-mqtt yudao-module-iot-plugin-emqx @@ -15,7 +16,7 @@ 4.0.0 - yudao-module-iot-plugin + yudao-module-iot-plugins pom ${project.artifactId} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml new file mode 100644 index 0000000000..568f862004 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml @@ -0,0 +1,55 @@ + + + + yudao-module-iot-plugins + cn.iocoder.boot + ${revision} + + 4.0.0 + yudao-module-iot-plugin-common + jar + + ${project.artifactId} + + 物联网 插件 模块 - 通用功能 + + + + + cn.iocoder.boot + yudao-common + + + + cn.iocoder.boot + yudao-module-iot-api + ${revision} + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.pf4j + pf4j-spring + + + org.slf4j + slf4j-log4j12 + + + + + + org.projectlombok + lombok + + + + diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java new file mode 100644 index 0000000000..183c76e58d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.iot.plugin.common.api; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.client.RestTemplate; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Slf4j +public class DeviceDataApiClient implements DeviceDataApi { + + private final RestTemplate restTemplate; + private final String deviceDataUrl; + + // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 + public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) { + this.restTemplate = restTemplate; + this.deviceDataUrl = deviceDataUrl; + } + + @Override + public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { + // 示例:如果对应的远程地址是 /rpc-api/iot/device-data/update-status + String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status"; + return doPost(url, updateReqDTO, "updateDeviceStatus"); + } + + @Override + public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + // 示例:如果对应的远程地址是 /rpc-api/iot/device-data/report-event + String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event"; + return doPost(url, reportReqDTO, "reportDeviceEventData"); + } + + @Override + public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + // 示例:如果对应的远程地址是 /rpc-api/iot/device-data/report-property + String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property"; + return doPost(url, reportReqDTO, "reportDevicePropertyData"); + } + + /** + * 将与远程服务交互的通用逻辑抽取成一个私有方法 + */ + private CommonResult doPost(String url, T requestBody, String actionName) { + log.info("[{}] Sending request to URL: {}", actionName, url); + try { + // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 + restTemplate.postForObject(url, requestBody, CommonResult.class); + return success(true); + } catch (Exception e) { + log.error("[{}] Error sending request to URL: {}", actionName, url, e); + return CommonResult.error(400, "Request error: " + e.getMessage()); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java new file mode 100644 index 0000000000..9473033c20 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.plugin.common.config; + +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class DeviceDataApiInitializer { + + @Value("${iot.device-data.url}") + private String deviceDataUrl; + + @Bean + public RestTemplate restTemplate() { + // 如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 + return new RestTemplateBuilder().build(); + } + + @Bean + public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { + // 返回我们自定义的 Client 实例 + return new DeviceDataApiClient(restTemplate, deviceDataUrl); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java new file mode 100644 index 0000000000..f9eae496d4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.plugin.common; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/plugin.properties b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/plugin.properties rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml similarity index 99% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/pom.xml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml index 266e45fd34..cd89743214 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml @@ -4,7 +4,7 @@ http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - yudao-module-iot-plugin + yudao-module-iot-plugins cn.iocoder.boot ${revision} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/assembly/assembly.xml diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/plugin.properties similarity index 67% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/plugin.properties index 49aef5b187..bcdce07c97 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/plugin.properties +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/plugin.properties @@ -1,5 +1,5 @@ plugin.id=yudao-module-iot-plugin-http -plugin.class=cn.iocoder.yudao.module.iot.config.HttpVertxPlugin +plugin.class=cn.iocoder.yudao.module.iot.plugin.http.config.HttpVertxPlugin plugin.version=1.0.0 plugin.provider=yudao plugin.dependencies= diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml similarity index 87% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml index cfea78964c..15adef3e1d 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml @@ -4,7 +4,7 @@ http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - yudao-module-iot-plugin + yudao-module-iot-plugins cn.iocoder.boot ${revision} @@ -22,7 +22,7 @@ ${project.artifactId} - cn.iocoder.yudao.module.iot.config.HttpVertxPlugin + cn.iocoder.yudao.module.iot.plugin.http.config.HttpVertxPlugin ${project.version} yudao ${project.artifactId}-${project.version} @@ -121,26 +121,12 @@ - - - org.springframework.boot - spring-boot-starter-web - - - - org.pf4j - pf4j-spring - cn.iocoder.boot - yudao-module-iot-api + yudao-module-iot-plugin-common ${revision} - - org.projectlombok - lombok - io.vertx diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/assembly/assembly.xml diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java new file mode 100644 index 0000000000..74c5651017 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.plugin.http; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") +public class HttpPluginSpringbootApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.run(args); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java similarity index 95% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/HttpVertxPlugin.java rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index d77f990c20..e6145b93d5 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -1,8 +1,8 @@ -package cn.iocoder.yudao.module.iot.config; +package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.service.HttpVertxHandler; +import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java new file mode 100644 index 0000000000..b5e977efbb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.plugin.http.config; + +import org.pf4j.DefaultPluginManager; +import org.pf4j.PluginWrapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class HttpVertxPluginConfiguration { + + @Bean(initMethod = "start") + public HttpVertxPlugin httpVertxPlugin() { + PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null); + return new HttpVertxPlugin(pluginWrapper); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java similarity index 98% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java index b92b0869b5..becba2a082 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service; +package cn.iocoder.yudao.module.iot.plugin.http.service; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml similarity index 53% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml index c5a1ee84cf..e98d46eebe 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-http/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml @@ -1,3 +1,7 @@ spring: application: name: yudao-module-iot-plugin-http + +iot: + device-data: + url: http://127.0.0.1:48080 diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/plugin.properties b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/plugin.properties similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/plugin.properties rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/plugin.properties diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml similarity index 99% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/pom.xml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml index 7e4689b4ae..e007596dc0 100644 --- a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml @@ -4,7 +4,7 @@ http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - yudao-module-iot-plugin + yudao-module-iot-plugins cn.iocoder.boot ${revision} diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/assembly/assembly.xml diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java diff --git a/yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java similarity index 100% rename from yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java From 88ef8ba2e3be7eed2add375dbc07eaf029df8742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 25 Jan 2025 00:12:06 +0800 Subject: [PATCH 097/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E5=88=A0=E9=99=A4=E6=97=A7=E7=89=88?= =?UTF-8?q?=20HTTP=20=E6=8F=92=E4=BB=B6=EF=BC=8C=E9=87=8D=E6=9E=84=20HttpP?= =?UTF-8?q?lugin=20=E4=BB=A5=E6=94=AF=E6=8C=81=E7=8B=AC=E7=AB=8B=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=EF=BC=8C=E6=96=B0=E5=A2=9E=20VertxService=20=E7=AE=A1?= =?UTF-8?q?=E7=90=86=20HTTP=20=E6=9C=8D=E5=8A=A1=E5=99=A8=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E5=92=8C=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3=E9=85=8D=E7=BD=AE=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E6=8F=92=E4=BB=B6=E6=80=A7=E8=83=BD=E5=92=8C?= =?UTF-8?q?=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-module-iot-http-plugin-2.2.0-snapshot.jar | Bin 10353 -> 0 bytes .../yudao-module-iot-plugin-http-1.0.0.jar | Bin 10643 -> 12284 bytes .../http/HttpPluginSpringbootApplication.java | 18 +++- .../plugin/http/config/HttpVertxPlugin.java | 84 ++++++++---------- .../config/HttpVertxPluginConfiguration.java | 65 ++++++++++++-- .../iot/plugin/http/config/VertxService.java | 52 +++++++++++ .../plugin/http/service/HttpVertxHandler.java | 66 +++++--------- 7 files changed, 183 insertions(+), 102 deletions(-) delete mode 100644 plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar deleted file mode 100644 index fef8d5362c38ea47e769e58933c715d492df719f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10353 zcmb_?1zeQd(?6YyEF~o!5`uJtpmf91(jhHfvVhb|hvbqX-6co}NOz}nN=t`;@WS$fkzUr?_<<-}A(n55*z zA1eM%h62NRO{QCLl-6u;D&D zD$7WRFfo)XBef=&t|~@Pbt(6cIZ>S!&c1a&@GWs{j6dauNcJ6D!Rt(I`HpZHoVKSo zx`%Bkm?atsM=I_$PnXx>D^n4-_{CZqAlFv%8nc-zty&=#3hzBU0zCr@pX`Rv>UHj? zW3e8~YLVYhZh~=8YijJxwJQ0D;Az)HPot+MeZr|pEHs#|YDQx~|DsU|Y2UnBIPQU- zyQ}(Vo4oCU*^#dHd0K|eLs>H&bC1sQ3~Nu=A1nV^LGafrH+ueuYJMg_|4v|TV`O7& zV*d+5tp6l*b22ut`2{(_-;rC}7&}>+{DSM=FSyKY9Dl({^)H-8HqW1#oBkgJ*jhQ6 zn%`;?<-09XHyB94!N3F~z`*eRcQa5iad3R{+hm;V4IIsFo--R+88|p3gRB*BM1Y>L zcNrwyu&~5LSHzeIX3PQ}ipT*CIp`&$M-G>cYnXz!znIuy?&+;(({k-0pQeYD%lK*m zh?uKqsy{(ZQ{0@toQD_B!3Z=<`@t@wb>OoGP(T7b?8seZ)@Z#zQ*ot61TXU5+4mFL zXRb)Yt`d;8B{1S7*1Wp_so_K3%L!Ua+4GSS&nU{(GlJe@_4J@+hv?RjrM>JJCp2(X zW=KUe?qw%1iRY^9jB48unlm?%%ba@}Nj-FzrH{o0TuDz%X<3~f!IUNjnnW?`5Ti33 zVz2&4F1;dDqPwis;04MmRSjf!#5Er3+J#Vd@i%QBfkkRUV!rw)3jxJ4&h#0$ox=yf9!%SWT8wRCE= z1o_S^jRc$X;OgMqjpRF7UfyC9j<8W0fR|LT5eBTS*!xvdMX=L+MNWyw*mZ7Y=o)um zX3_c;e)xj#1}YjYvktvB&{0Gf7=iys1EpsDZlb*^Pp?fB_Yw(hDi$#|e;yC30?}B6 z*+&ff0g)5~P}+6ZdV-{D(K)-XZ*2E-uve2_8ZE6xG}K-sY+9{>E|yh<%KIeiXeMN$%yG_|HIWZHa;zngYIQqjvD5n6 zEi$T(jak@Q2l7rn*c!hEqTzdYWJZd12~Ua=%OY8(s#k9xyfH4`*+Q2XMh(`TU#Ee5 zmUZU$zvD<$BI}rTK61P$k|PFn?hHseL)#0wzm2hiSd$YozoLy@2Oyy+4#HD9x>wr7 zsgO<<2LQwqw3t9rkURn6nOq0(aDBM)Nx7ZcT_*2`tAZ?%iV~~5DIi5lOyQY_i1#JZ zyS_;l?64i0>Kdav@zdcnSDEOyv{+9Y#23k|7A>0WY30R5_u6YBIg5Rf;V0WnqznNE zO`%Y=V;OXmym-B)#O(0tj3^m1fjz;G!<$ zuF3`TDWs9RH9w~C2%R>PVny+xxd<==zfSq1QwCjv%N2Y>D-uSvXvVM@A-#mak%ik% zasIGqZ?N9quZbe z$y?Da^;H@QtLQ_0Y=BGSgZyK(q2>uuFXA$42`OS)&GHP1b|LRjPwzeNX8Um=r|6k`JY8|TQm0z2bdaj~n& zEQjMMpdTNQ&Kp=k#h9H9{t(u9w zqpPIBb7LzL`|tMNs{Yg#XPodd4E(8L;$>mWOE_3w3i7X&Y1-0O*c<3bzBY|*95mMD zufsAc)N8xGL^euIanMVdVG|`p$7a1hlrDyi6P@sL=JQy~@aU1v#`LhBu0Nu|^jM3& zG^JTeWJv+A_ekyuKVST<9Ymj|-c}k~% z0_m#DvJR3U?0Q?ez>t%p3>+yzBWe!wD@>GclCoD%?V!Ps=;!H7YF$@c(`1Zev;Ou5 z_{rc)P!uu-zqwu9LJ>6?H_nG)p!m}<rMUZNZ8BWu z!HTw6(p(K0KJ?FrgoMmaMdQG>I>3tEecnc3VB3q>yw3_NRrer_v3E*$*wrys%d3=ak ziHL!%P*3m4@B_`fwF8vWjHhJ1+jp>xsC5+BL`FTuoy2`-xh8zS*_UzgZPH;C0WPG& zjU*ZZI2!yCW1%uAX0Txz!;}D_Ez@ol>3T$!SRq@(fQCNTJ0?lVL?tg^@0gQs9?o$X z0cl=~gvR3_S4T#Vro9)Xjcu|uY3mHw93FV+D~BiV{WQ;0wiyectZmCrKT}r`RFu?F zJv5UJfMutrAsgN`7m(*Ln#C2w(XX+eXiOV1uA z`x=WjYf-N-7dEz-rDaNSMY#jAgFh>;mL2L+xl34Aa2N}llr>@Ahj>rjlU4ap<|^%- z-w4lHn@ncPtgo?i#um3>=t}Xz9fNt{3Z)n4!fz=|X|%&Q=h!4!Nn&UBL?zZf_6;_h zJn#AnHmZdOgFKrRk@P`Fz|u1SmYq0dkpezz9g2%idm(6#qZeOU2B`ND9}g_mTz<;- zq*wca3+S6%CyII-24nlV1i@GrWxZchabUmYDVn^}*Yp=X1C`)jokr0&i(+LgQl~jQ za$Lq}ayqTuDkSXM8Oa$z>X~-aM>$j-Y8RXs#S0Gv70#n5nq$oZJIxyh#)ydTEINHi zngDn5z9$)tsE@CF zpWH4p=!J6a>9;h^4a%u05((EP&N4*xI?k9Xf)*NChSS0%{D&ZcUZ~0BQXj#?8chNU z8-%W}UuW;4qCZAwsACcgV~_1-el*Rb9}QAO-CxRlZ4nr7r@0JCjzS{T#hl~ocq=1& zRDXX`Er-MBZ7pok;Z$u$Q9(OEz-f5 zSQo_MXllmCB8~1U#j*D@lbC_&Vag0Jkez{@I<|7ND?y@lM)Pk%g%n>Yr7RJ@6@SDG z*kC$SSFn6JrvDAEHx5j|*w(G~bUNcYH-x|%RB5z|ag9|Y zhr)53V+>(n3Yd4q?SV0@(LSI|%ADGU1fd_PP|^PA9ouzA*u#M{Yl>IGb!u7w@hASZBz_H0J>p**Jkqrz8f zjuBNU!nmj}EC&5e21JJ{mk6cnOiSUjOK=_<;On+NPrBbH!{MNij^QQBe<`~|OQ!#l z!)4KidT(y6(Q80vZ>nA8u$)K$LKa11P1hj5$cUSRmWOxIa%F`8D%oyWQv}wA;YN#v zug7xh)|1lBn`=UYFHoBWq}niscdyV zqg6Md9rvHN0O^u+vfot`N`2bW{CL8v9hhzI7FjQ`?mpR|GhqFuCbVq%G1jXZ!?&uV z+!wH;Zb{H?c4*q0=_IidqUG-XZ%c65VA&)JCp5i=5F4hI2kj5Rb4hhvILC%im9e>gP{! z#|b?#$>F={@bRL}U}P5IHe6*w^k2ip3hP9}8HSn<8=RDc0N4|unzo4(Tyjxa79+E- zClfNpGSZ|*>AK{MhMo>(nV$=MBi4BLXywt`@J`PbbDWN=vX_28)S#M?t= zOic^wrb0+U3V4}9&qqNV))=xa@?@~*4dG%eb}&Q*M9A?(DS0?8T~u2)bJUJfaN0hM z2G3;kS$tZHs^R8inU8>1QeaXwmT0CobB6qRE2oxdihhQuG;C^ycuxi&KZo4wU1KLy zJBODTrBEGIHF!%UOFfS*P3X*ws2*x29e`;rQnck1SC^O0CC))MreEl1Pk3TL@&vDQ zEu1eam+>OrDEA=QZD7z5iM}V9w&-;sW)I?UL4M_oBKzvH@QxCxBaxr_PMngrG-M^M zx{HwX&NRB&{t6N#`&nOu`-wkhmQ700)?#ecmt})q=K_tDnc<~9_F}a@qr4D3PouNr z&VHNZ8h4Y6KL74vvU(vwyMw-t&eIX->1hR227lf5nIrY(P;S4C;6pfNrX^UcP?PC0 z*?bt{g*Zj3^t(;~MothRwzrB;Rx2KNmMGtHp~+=}FcRE2exko06K+=sF5*OxHFjn9 zgm*GEo!FB-MTlGRkhwTXB(Pe|uzWOCY2Y5rfu;@~X7_}@1WE~`f7)~dfnel#x=0f# zG%pM-hcC21kMl8KYjG!_RjW?05nW3g@AWq*wW&UZpq_ZTA1z_;M7LrB;hhydFXi@1 z|JeItOcpYvT6F8U{LdJVu!7l`*0z>dGiSTYzHN}JCwWHhf&++EmSd%o8w2UFhMoPU zv`pSD(eYL+1@OJaFv+}QDL@L4ea zy)?<_uw4^Xt+wOgLN=Mld%P$c9IuSQB(Dg^9!m9=G%favR&QspEk-^B7KQ}LU4U z>99VA1z`jlVFe-XymT2Q|JZ5_Chk`jnwW;RtEC*jpsw=6A`0t14P%imb4_jCzJvo~ z_RE~shv4tREA!1YggW=2INS5*fiyo)4eCc605PkD+}JQ5DmC9-n6fWmyIfrJ(yiq< z*#C4I$bGQ8-xPfMRDDO+>nR;S#S*oAV0@ku)^M5IGv|}1pGzk5gD*Awh8J^rniUh% zD2$Es);pWS1M4elYt!vNUoh_B;4iL(yPaxOcPs^t!8F&PBxC4P$1q7+@_V5c4m)&)aL9bZc*&t)W8&S^hw zB{E^yHPqXv>mcmrLp=Q!a+fkTDGWY&22c>1g^`CvR07DBIHe)ZAqUwfJVEBtbWY zLyUIgd8viMDg{;S=7zqFWZ~VR{P(QW4JD~Poi0IGs_H@B`f4^N9aA_XI8?>O-62{c zvu{y9=HGQ@!$M}O@8lp!NFE7TMXY7P2MJBLFrkulRE1&^a`ixD*&#i!MF`rNEosLz z>DnwjC!6KfcJeH4XdfVE>d3MVN(9)=9qTNjAJN-;Cs0P|+jp~(+efl^oau+k`awy}l z$E_uYGF0+>7!3-l+&!(wM$nMX`o-FsY~GUqO4ss31J>AWK|!&dV7qArIGxE@zw z^;`oC#w%lEp}snqB+a#ln+^)y0rnWP=`(DQx!Slm7Rd&mqd5d&AV~vKuJKoDG$~N$ z25Pj2-zaxU{W0H|mk-`znFY39SJUVus(>qe;+o+wLbUJDgIBKX!M{h zIGWKR=XcO&?cTZL`*>oL09po;{TG7{K(5&FE!^dHYk8JXVeN3d!`iS2^*7NEbQC-% zC6B-Ek>gHoQ$Bj9&weM0JEe{5g?r`jlPdUrtW}f69;AaP6Lsa|iYR??%S@KE@$&FD z;1I)7Sqf?v+cHMPG(Q120dXLy1Z!_Ko|#-w(VP5}z!98pW=)%xHjnuQ!n}obwh2O5 zHH>)FxFM?>vkHh!QG{olIozr+0(VtSy{z~2QODwp8p9h8M!WTH3eGsvOhY&Ac_K=Ivw7g|8p9#9HQ`_w`iOEB5?`9q#}oLcn$*aR^t9k_+mNEv=CO77`Q zKP|t1PTsQNgCPg+xGksq^rqtcZmX{CYcetJj6;vZV%9>2P@836Wf^IFVPNV9L#5C8 z_34$+#N@0hX-Bt);S@4!*?b+aEcnbB-=ZW&1-rLR$&%)im!GF{ZX?@43iWjPRlDl* zRLoQ@mo@~}No12sW|Mcr`q+t-JgxTE=HO7BfrH!4+=zFJ6r7=#m7VPS<=YY^$S(#I zjftIDngr_gW9VROo`SrqM{~aUeSMU=RuvnD+VHZ1ysbo6eKKd8w%$|H6MptjiL0B6 zm5uq9-q7_8#=O4SnAe{Mf4XsQ)>fRMBJ%L80GJ&&f10YSd}U!(dC8;~IVuC5bb2G%$>F@{$CT8#+qD?Gd6Y*> z)6PMbTUj5}vU<^y>o9+QY2jAM*HBf!10ay%##5M^qHgT#NU5h)APGmO_Q*F0dWBtH z`GDZwNXb_!i8z1y_VnpY*YN(U&Id#X=s)cI=c?4r^8cvoSsOT;+!kJbCMNrt*yuU) z?-ecPn@8XmWuKd*_+OCz%KTfYi|Kpqis`xv#bkEV+SQ&C<%95s(DFmh37U(WL*k zNDZJ<5NkVKqy&F$9v{SCh+3v*&Zx@Dz2h8|obM<#zFaLCSQHwog}+Kr+zGK4MT47? z>_Du)_<|_(1cw8a0fRH63WS5ijs~~*k=+^g|ukBk9r~{=r za2~f;&9GRAT5#RZMQW6TVqtlYvt?u9@GNGW7cm%o(EY06 z>g=?A=j!6v5;z)TW6bKzv@tEMq_``m8U+teSo&a3UcWU(OB}t6UEgx$&G3GPpa=2n z9+_%OBHuBoq-DxyQTq3XPLq*R{3zFH(+1c%VT!ui!#!f;lzy&tA%tvPHN4jq20$)~ zZj_|$P_>H5l6S`n6mOLkF zZE+9EHwUX?9vl1Yw8N{$Jf?k3%dZ*Idq!&3W^+a|9)W8G)Ok85QA|BkvT-t2>vbXx zt!P;@VfXK|*sfG*-hTa@~1Lbc~LzMYziYC^DBgj6Rl>zP#P$ zA(~^u^s%BM9+EYb=o8#BdL|T-MPN-Txp`s5@Qr@%MtCkehSMY_X{qD1CT2%}#8D4c zt9U9psIH$lGG;wbWK5-%sG9K|6zh2|z#E6nKK0?L%@i3`f_XuGR}{?<%oN%-_#<1t zSoVqgy{z}(A~rQW9qt9o4=%kj7)&TI!#{775`9+$soavJB}txR?Q#MIMtCGlCL>59 zJJ2ju3A&&7>LkG>`N|5%CTH= zjZ1gxn}V;ZSJU;r-Ogbf0`H%TE~gZk_cl+ut{Q+z{W44Db8b*FSvxpV~ieCH%ap+%!&nA2+}h z*IV*$Z501J)0;+$TZYHq8E)Gu{+aQnvEr7|@;@1Wv|0Q!`%R<8EqlV>vH#I_@y}B3 z{Sfk-o(Fy*<$D{(KTEo4#P~jL6o+;lv|cOjhaSE!=lA*CNcy8Kzt8X=+BR-Y=V7K75M)`6F=ensJ{Od>91wZTO{)9B;>D< zepk`o7d`(9^Vh8AElm7%p7VbL^Zi`%J;(W1l)s*FZc%ox?fX|K|J8}-&v1ViY2U(q zBKj%rzGeQ|OWee%-vS}`cPk!b{)cV5?e`dakTW^^WuOGoboYZ&bzXqben#oNZ zdn@emzYF`9sP@OSzx&p|#_rP5zhAu#7GzLn?Dg5lRtLrkGR3T9y$&OJs!=@Y$;>3A-qB(P%e-k5-qD~7`F(zE z4aL_W06455XChFe@T`CO!DN3J?t8XfUFzQK=*n7)E?u%rF^9`;PnLBU&MMECoG*LA zLL}|9`9MwxF#;mvCJqxa4~Z$h8a2{kDO__|U4thRFbn7J$Uj(o&R2+S$L`SYT4Gic z-uH|O9gf?+D#pvk2`~=OMspK3)$XYa`P%CI#oOcI8Fxg_@#1RfP191bY6!`9kNnYb zPQ?uf9!ztt%I43fsLV&{a0bUs##h zIR1qt`TwxAwRAEu`zuG#{5=3uM@QSg7zWcnKryhfHa0W)3ozz?0Onw1|I*Chhi~D3 zd58SE2XPPY)_~RB3#9lD(0(17Lt1rcGbF_%2EQUVjqc)} z%-K7Nd5*P@BoAECI6(*0H8(zqNt%3^tgrUWkjbCzF{h1Xm<_akx6-h(*z0 z;u^cW!|;V0vTkv*o=1+@JQ2dIIAGQy_Q$Xf`GyaRPD19KQE(z$RIm|jC+gx(wvnrI z6v8P%#UI}8L~MOkI(s=1_HK+Es#GoaxHDP|lpQLL-1w!mqGnKkw)|s9x^qv-&NHmM zt|`U{`i)Q0H)>6O7!-(W_N7}fNOo$bJw=%zU5(c|>+#+0W z_~zTQOk_q-aX)O8Ca#HBJqtwH5{z1qw~x&L3p7CbOCBr~=r~KU#zb!!R0&GzomU4# zB9En3n01X8IHwfwQwQBF7;_W%j9tndn4rCIgwvLP+Zwp`eh8sruRxAv$kuNRIJehz z{C>0@DC4BpaB)HWGT@2del&c69VYEcav%Q?Iw9H)naUeR{*B!I6*IPNXsPtnX#xtJ zAwq+K^aFh#fZlYg&k^QXD$N&K53c?ln|GE)dN<-XbL{~e19b!SeXIEvU#ibtK6yeyKsAjA>TyOUa#Rzows={%Xx9LI;aXEUp*d4OhHJYb&?agoQz|y<61(>A-KgJuo74@ zhCqS$Fw#$M5Y=@yDaf79@{*xL?6Twix=Up%XY0_Y$&43YtEk%Kj&J<9WcH%aOfQ5c z$SPlhFQSg5Aw`{Vfa}@ohHP&-{JUtJrqa&vJ@m>ZA#U=L**d1Al%=2s;6+DdQ zE3cHZN2~hqFF-QVDIBz2vdeh+^0+0<9;YQePWBF`S9!9PCJd$Qrj65Sct9iaHO{RP{b9(6jv(tHebUNfnwj zy`t?v`$h+~#LIjJU(!}`Sq+Y}fbz^uF!EN)wyC`J%|h~$;qNAa&u_Am0A34!{Odjr#!#Yrpkd(3eyv&^i00IgU+J3f7C zeI$$-{|TyY!Wy9&8$~EK4-V~>F2cW$es!=4m-?ric7Od_9W>0{0y`YlDKqO%^z zU<>G0@vvSYaCPXp6h5(cUuL7icvQ8usA~{MOf%62LZ)*h=VbPk6&`Yu`lR?MSrk38 z>U}+++A>(NjZ$(^{`U*qz%$AcEY^N7f{3Pz_tt>50BS4o_Fl3%f2xJF6%qT7Lv35 z1^x|z4cB*rH>V>buOBpp>qA$>6Ke;SWKjuGm>@mJ zRY*p2$?Z)rME9$8OkDj4)gwH{eEY8-qK#S-a_F&)$YLIm`SUOu1kn#@Rw&U`7-)MQ=PDi?v>7VPNB08AMd<*D@0={S1u zr{1WsqzGp~ItpU|6T7CKnwvyTvErY9^3)2Ct0;i)$wiq~* z%^BlbTJ;bEo%HK(%%A!^ZygqlYD1=;%XAwM!SLxd&G8YaFlbz^P>SFnc(O98 ztDHACi;8U7O~$?>)j5txd-KuhqBz7C->Aa7X@?{UwF&%+(luIB|HaDiXrcpOhc^G* zr5FtGheosE zAlIBF)mOu+M4z}vmHI$rGU+YvpprM}7!jZ99Mc>rKoE~!1x@HcXSJ0^%kONQe306~ zE+?uRfBlL+V|qLtVTv(!C0nkTaIJMi7gX9i)E%N0>lJJxMOR z6xcM!kU5kapKj_9>qSrJO<$9E8*T>2((8z&LaYzFc;|wniviruA>6`4zOG$uQb|Yw z%|VZwUeTH#!+wbC3PJBu2&fn0q2aH%hIDX0hU1v)@A2vd&+he43O__; z<5*)|jXU~qDxTou$?l`t)Qv572riZ)^+0*n39 zEPSj2fp;jz4YAVu!S4go01bV)?A>lVf0rl={qunI%V{3?Em!z88mU;>D2V~Qj2ri? z8XNase)R+RVk25MzCwaTw0w{a&%nskkPjc^=I-3mzBkW1=t)~1^qB(G^Oj3QUq1|A zDvT7q=#xLmweA%`=p}WS$-Z^#U<1HU=jHxT+A*I=hQs>M&EaSNMTlK+J-jgd5?IbfCFz^O2dM$7YbI>D%^6PVX{%+W7*@a$gV8=mlu+c3J=Q?-MDGqVmx z7NMt13tLymY%QpZd;8YBqL&o(5NF`DWt!Gx)|mHrYX9cAHxgB_J^J;HGC!=XXy;?P zh!xy;JxVk!yon4nN30biXX73R8D0@^QST_G>CxiC#n%Prqo&Fd$>jhgi7)v)rILdf zYnyrGj618?@&i$F#ZSVg-X`!yWIe(dtYX@s7s1z?) zeD-3e5fSpl>E^V1?`mUlcc;x>nKd)|Ss07ui{$Fj63CV*Jhu$LibtZ*8+JmS1ayom zvg29LKj*eABbY?gWGpWTBXY)rS#`H6q{Uh(J|SCxJZITzJ>F)pXvMBQF`Vt1;0I1u zlwn94f_v*;hRx1p8^=;5k{=qWFV|>&uD&i0d|!f}^>}DBj@8O*d?dobM5(|ZaU1#z zmDpw7{^kq04*-ewovnz*(KxaSYHVJm?w~?~3F;HgvDV)4+EugmR>KL|L|d&-^>nR_ zLhR+C)}f;Fv&EFB1S*Gm!C~-=Ea!^A8umnLo76b}6#@Ey4Y!Dl$AoXJN`x5wq-_CB zbbRMZUnoDxxQoj;Fkl_T1PW9~fiEpDc1P@+L*-!nge`1afx-+Y0qDsQh^hf#|UH zLs~E-&!Yw7nGppFAk{v`wRm;nYW3jeBo-nP)#yAY_YO7YVDFGMkh#f)7k4}HkIpz* z@#1hiA}o9%7_Q9TK;`v7`eY$6X0tUC)bru1X@|%TGVFCjEZnF}0?T`JIdanatcPam zR`8hULC|#+L@rNBJ0rm3Bi+DJ9XOekkEnAu*5Q7Mu*!7sT8c8uw6I zo$9)~X$W^4w!ye(pH%4hJDy#r<5xb~(Txmkp9V8uYo2C?UZSj&rZRL!skhWP`sx)N zVKhEdnef7?)Q@5B?K&YBNOq?k@EDFEc09yKQKWSw!xQkNn=7f7$@kDv*O4N(#;q({ zGsmS!nIf<57Hd9kUR!bkjeD1eEm9qu3#Sq&XLAYb7}`Qh5Vb9@;6|{eUEiwXraet^ z?W7%DZ8jNH-`Z0exCAWNN7s6$1wWkX3&G$CM@D2Uu=B&fDfS_q?N_XGGaoK3dW6=( z?nlA;z3W{RTSA{Y)uuJwt3E%I0QN+#Pyn)d#vg1oElX)cBl(LURQTG5m$TE_lx zW)!Q2e5C>+1jx_fTQP;c(N@c%|e4`B)8ve6?pOPT7?+tYd*xsKsQMo*I?n zzB<_JE#~wN8rJ0O+>&-gXkkN_iq5fND#|&Y)oAK{225{V$1e4fc3~BFFuUfCC4#BN zFXqKD=dams>!1*w`OcctHmcRuZ+K9l?TAl}s(E30&j0{k;M2f@>14?5Q{-}apJg8_ zrY?*Z-2t@${sAIZJA^L+;qjIgmWiiF_M{!=Gksa|4oL;bSif3G7SeXHz3yyIa0@!A z@3bs%Lv5v-aAxj!6};7jjiM{fkKIf@|`kI#nzdvP-z(W?s-lcqunE$M<@x2QBTj5kv&)U$^$o|_=t5;RSQAri==4{QL zs{27#ohB@ejCxjbz#1T54a1ZGlGGRB5kfGQg*$64o5S0okKNqv!lUL}vgp#G>U3hg zGuW&3?5|J|dezH6Y)ensU#y3sQm&Mi+O+g|XX3rJk~C><*&Z-F%V@uCzn%S}bC!AI zu*Cq8dKd`dK{KIHkqa4*JP9`>G$EP%vTJ(Y*#}G^@`-A&VXYn6s6IM_?p!gSG|K?R z5%krdK`%Hd$N~H$Ej+bLGSr2$*Gv&VS&38cqxkcIuw{Fb(Ga`bbgvUz36%QOmwdfM zpk}*@mklZ7mi=nT1Ua?yX2$g7-fyOPE;MUNj&ULJ0woRXsRjAqCe^Y?i&nAc2CBv} z`m$Cm4oJ-iS3LW)qvI3OO|csCC_yD*w6X3oU>UZu;&8d?Y-t|RDZ=FDH89yqGLA`N z@_NVy1`#Q}5zS}J!*1l|8A(Sf3oqtDt>#xWDI2=n))Q62;Aa7N2_xb|S*iOds?$?i zK(>psB-89+yhN(sUtr1i&G=VTrTu{i2=b3V7;~`J=M;r?%l_5CvwDRn2bKWV->kjyfnfX znlJ6AI56{;%In1-X(gL!N^*f>+beb4Z=@cNXHCUOr8JM%^hulOxj&qTa+)WoF zVbYQ0gk9@^uVJN7n-anz&jVqeVeC;FZ#4nL1EVes+Y6Vf+xf@|rIM{IxVV*f2Gwa% z{oyRg^NnvK+nguMS~ZU7rAh(g_?AS+k8sB`X`DgSA(scST%AHhp^x4VO_D}ax)>_a zI^snDMDxs(S$T$sZpAu8ctB52E4%y=MNSN$5%c$%R?#2oj2$ZT4OHEDZwcZiJXuHw zUKDA?+X)Ehg>KHvN2~+}usvnTlmkNc2SIh2p03AT))ag6(H^hbA#!pNgetP0t@f`N zeeoYNSOve+#^Yx5GJK-racnddS;^vDe}up(l>O z8L4d=NEdX$@q?I%#6!gR`Yo$xsd%wnq%x|CWE~65Wf^6<76Xo2J?)1vY}Q;TlG*C=5L;p%~QjM?ai&DO!m33b@#;_a@TJ}oJ27k zdk$U|869hVbR=1(^V1Sf37~}w4AaM1LDK)SUdjD7@ zJ}A>0NU!V?spRwmVO9wy0;5Z2+f)(C>QZuQUK$(0F}MtK)hc1!aQWnsnNpkN8H2E- zDvvQ$WeM+V41))nU8(qtrPk}J(D4Y>^zI7C2Yx9Rup6+jaXM(hCXZgOB~3UI$vQJGZ)k~1qZ4NS>PYoG0nI>=fuRER9Z|Oj3D1r{QhR^JB@4Z=DIIEjq#j;3jd=wt8M+lr+O!n5^J zHqf=eNkjmhAL=JvPUQ%{%3-}M(mK$hs&_5EHtYFVsTny9b}!gBRc! zS0syA&!-(>yQdfOXoWHrzQ|ylbmRGXJ+T`GvaOw5Rl}mYQ6VpTUL}iXnk;T?Wh2s4 z%`%cAG3CFW4U$u=cywEyhohdPjm5FqNwk>@W&qPsnAL5iJ!P0ziIjLdA~EVyI2x48 zTm)U-jdY^!fv^({o_B#GIAEMVk-%S+K(bp5dadAn&BeY-G9BQp&xSPm*qX7us>Dn) zfZ?1{YBJ==f}!zu!%DZzPrCT~S6mHdo9*JPW)lr&$yCH+uQx*=b+vOl3$LXJ$blu0hdgM(yfuNOidEctQ@t`yQZQSyFLhrL=L-9y|&ZLPDI{ z!7hW{I%;W56D)4nG8`2QA<2+1T<^C}`+{uuz{bS@1t*(PQSt2o_QKh7zexOdURNNQ z6OF3~AB@Z2E}y?$=ae~VQR?0$Dh_ua)Vq(~e8iSK_K{~~>0_@-rGxKkxCeA5;%{7xG1&7s&neA4&#@91|E5c!wL zwl-FbE>@Pqs>9ZEoM;_iX`wq+qb;au5JglJC>Zb`2|cXiaLcFMXEX=~NbW~koR1pF zi-kgPpN?vLg=`0Sre5)ji|0|qsPC!BdTwU)EMJXqrF}dZYoN>Jhz~*VBF@ZuL8zD! z>UY@ddbP);Ly_4RS(96rumUseRHwWv^GRAg+JuaH2A-k5VuZQkZHm39Ipmb&TbPdX zZEmk1V^eGnLh~@AQbcJPWO6?TLSG3@Sp@-a_lOW`MA>%PnU>BDjd&Lf$g zR+@6SswoF)eRx3y@ieBcW+EeDeE_&RP%%}EeL5a+OE^UNy6&xgM3N7MVsJ&kDD6HY zJwbHJuvCSwZ7p4p~wA2S$Sj)D^Trv~iZWRrv5MIY)%4 zKO?Lk9!xLf4C*o)md=TbL;#`~4n7bKSMQ^urNQxdhrKC%4x6^M_o{iPT+PV$ra~Cb z<2Yn<&BuNC4GB_YMkBSxQ53Hzy7YkEC<5}9sO#*$|T^Iiv0ZXS=fQtHtIvsbd6 z1Lur#q~aEcPbO?B2;4ZvbD%61ng4dsdIoR4nk`4+-J%3e~ zkRtRn>htm~98ahp#Rua3LL||~{UG_K}p{9zP4#W?VBB+-$YHGco z$jXDH`;%C0#9e0yCsnx!Z&8fkN+#bJDJ(^b8*!!Rc}kQd91xFCmSqr-R)Q15(E3#< z%${I|Zc7CTbkzIx1*jh1Ua=FWgw=VYd^QolPpjpI(xn_GN%xEM^M4t6GwkGaAGwzmDA~Hz|WLt)o2}lH4g=MgI$^Q^p?YZ0|}N&-w5q zIyy=7r{$rZMP;ydE6_sA(i}i_Y|^LdPNN7#*m`Ul8aZ0w@wWK z`hqbta-5+vHH{3lo~76h3fIQV#m)<^$9E;O2ao`$KbQU8&GD|GLG0b%cKp}`ewh7t z8Q^{A`^p zX~>=9zWd?ZbbcMrJ<@NYygvuLm*stH_l`CAdw{?0#D9*;^d0n29i7l-_9QQyz>KdXFx(C5MZTl(*s zpPxB@7GC_|e03Kq{@s+nRpWkU{P||}2P5{~>Fpmf{_&pmXS$y+2Y%3X-N~5$F5P{h z@qb-`XZneOjuoj-H_oLcYQyZE)C zLEPR#ko`w+|DG89`Kx)K8hmT_#teTn{$rx>=V14#!VfU>{|)v(5{K_h|2=j1&Xwoe z^DmixO(cFe`M;kmeh^~cZK1#Ipx>PQv!DKQYWIXc&KU~QFn9N15D++bKZkem;BL8eUCG74maMrbLY-K(6qstBvJyac=A?_x+$ zz&o*YF_-1ZyMJ3lLqXyFSxinuURpw2MU_oX!YfM728I(Y@GRQ?xh@5MWoXDWv>qN! zu9g!yZBTg%oszc%ysGgDIboK)&qmpNxmh%Si+XRg63v76w z=TnvW!LW*z!=NA?2f8*F#{ue4fEAi)eXC^2i=m=PH+#xPqqap3&Bq8rJ)I^pPBNUF zC;emnW9IkK`^VD0WL$H)KyGIUj51fGJP+ENKMj#LIpm!~e-8u{6v|KWg1rlbk^*UTIRNnztE)q4^1Om z8xwQWUl^eI4+9QhduMZ_ADc(|E!L6VRQAtcprB;op`iHx!@}=dPz5_UivG46Cwq{i zxvdSGkrl|nAzjs40apae8y}?#5lOwotR&MTD-O`zgsXe{Z zQ>@!~Zwo_QpJ&3o4;q?Om6u6UX9xFo`hmy&d$VWcPg|f}F@!_0gQ^}~aG|vYeYkm* z`%bh4_kK(@Mc$#mA`iH40k?eGu}{o_S~Olf2(%tWZr3uP*5}jU!vNFHar1=9T%RK> zfxr?iSS?%H}|SHj4z6_ePv6_UdJ@a zBAA*e7=_o9ItERlH`FI!Ir{SzoA+8Sy@;MyA_KKRHl2}loizi1m>}p#r!c5~tvfoQ zXnd@Z`eD$e|3F``5e#Y4B|E&vww5l9j47z{pjDJC(Hb2UO`*?}IkYKibFxc}Cu`tP zUcg*h9Qitrn{hUZ9MJWaw~4l2WXgar6n&B|bQ=v`C$^x@`O}%)f+d;wV^cf-Eba%z z!E_2A^KC0S0k+7ySpjL!sn%IWXV4mTNnvDJC!e1Huo1N-=yIvgnBvi{^Jy>yX7A4D} z@Tm}ku}IERz79S6dz4x`S(^Jv*V`}Afn0PPO>v|HIEZ1P$ADP27( z9bEXxLjJTHZv`nJnX4h9FMR-)M)A#IbGLBcYsj~}t}iFVV7|-iayTd`!T-y=rs4dS z*(EC0mR~cwTF#-zt=jZs$Jv=`tf~hqUZHAWj3!e@b<&6Z)CQh8`?_$K)JH8BiFm39 z{=%)~w1d{La*D-pn5QPr&g;{r`lnZsZx)~gyXymqg6O?hrPqodbr9_~q#Ab0!7wb%3+cs61wkS&qC6-g>*u{UG|xXiP5`Y4ip?kLQv{6==(h(W zOnNr36IEPF(XH_qj->^S;8P05Mvt)*W~k@;7m#roZ{fuuwu>X;+ut`SU!=fH1XaiI zvv7lHP7#_Sf@d0WQGy<;TnGxTmOnY8HlmYz4su|kTXT}>_G0}Uje_CY@9{V^>BBni zLp)O_qbWY&(Q*q!?Qpe5GSt#elEc#B`B#<4wISR1rE-;(PBCV~4iH#gMi-tBXr1|d z3B3Zr;9Q*)19u(1IIG!rLZ~i8_&zI3iw}Fz)P;Ehv6oa+jLyusT;vXqo0r6M3Ab)3 zfd~zG2<7a~#vfxx*aP+D%oP=$N_ZFGWZP*wpfdX;uLwI0zB76~asU_G-lSD{W66&@ zkY{TPp}NL*~0CyezXcVLJP;y<*@U4krU~ z<4ABfqka+V3bb!{RHC%;!G{11MVGg&^ge0k+_ zx;^P&N;hr%cADv;d37+&V^FB&xY6`z-bb<&rWWxwE~>rkz9B;j5ogY<^5u%sxN7}? zneK*|V_dL#r6s9M(@KYS-P-5f;T6&pO3Fx3mbnu{`Kc2kK)}RY;`zuj8yTW#sf{y7 zTmXDx32JrO^zge9;vo0|yY}IFH~-j3a=xOa$|Co)4VX25vyHJ?Gw z8quwz5P5M04O5@o^JKj>by6Zytr8*&WwuV5va5Lw;EhD5jTF}7L{=9++CiNxCA(Pc z9ZuZ{vPl#4*e%(3gC05mz>9{@UXagm=`vYGtkL_>@#sYkxvtw2SKFL0xi*E1*@`E{ zSgGaWZ9-^ofw?tdGZ1!oYSm=U3vs#$l#JxSf_5*QYTW$?)O?bgYt}MviCn}Soek*0 z*=x<5uQ9-a_I}wtM?8c3*xea}yda?efT&cLXMyu{IY|N~0ffTD^l#Lgs9l zQw{OgEIOt;uj1wOGxqW?l?wD3qg^q1?*(cWbfkIHntY6a%)9~%1`jEkBf+$XJjvrs zw?Rg{QRtk~PszW0W~+dODFV384F?%9Skfn#ekw2&_mcoaDph(shOtA$tdUIFGGbEF zD~oeA86KrSU=T5?rDo0vWBY^#WW9l;&B$MC*53CFhJaOdAM<8Wwu^-w^@cv=*<*B7 zc`=3z1M;U*eW}vvAbMW1gkLpkji3HWM&_jsDl{mgnoe3eG)JPIcs$8xbags{2z3#g z>3JdPY*-+ZY=J2uRl(D!LxyIls(Zt7GbL-AEO`P>83Y#}HJ?mU?K#P7REe6Iy+^um z$(`|@>9iiP)Ye;e#zE^O63U7c|f~t_XC%=kHr-C$)+j?O;>he#7o!zD=WOyLp5Sh3?7vI7moVk@x<* z5MUB9Q-G2jom^rH{Fg0vW) z^I#dfEI$Y*kBW8gsGSl+FXA5K;3R&i6JG256nCWulPJN|tXW0Uw|4?+P&tlk=}EKW z&3ay9AgX?%9T|Zba-p5lV+Z=Rwt4x!9e~>ARtrt(HShb+JC`bt_ZXP#QRKq4{6r)a zG9FwR8L4cnZ+4EFt?Ob5GJ6!+6c?ioAfBbN{NM;*=XsTj?Fu|PZoHZm`f2GJH$^W=2z zEXzz)JtG`HEzi2$IENPnxrB5a+;l{p$ILJ3qgEyc$-QMJEAAn#llG%Rsj1#9>}S*A zbE=UaKfw=Q56p)7!fky?HneH@McMKF)4p?z4C=0@fEB*XQ$!+aw%s!^qbL%z%f}8^ zI5o6i;w3&|u2CWTyjqV=k1y4TBnp-0^jaIXO+HV{=6x~WLHx1`4M_14Fyeq=e3>Vb zm_hrI^)csee|AlLWk@Cwtra0H0q1^WpT2~Tf2E%$EW>a<4=7^fu+7pgFrs|I+OyYvB> zCk1|dzDVKG$c|BzYyQ-q5O7={5L^vj&BO7KEEU5W_fuWsvC*r*ed!EvCUVLV*t9Id z(J7>zh(**XCq@izD^FO@3@;r9WS}<>zs;hGAgh3&54;ulAj`H%?GCjgtVsm|?0$~) zScKXQDNsdH5#Dfr^wJygZ3ma%5Nhr7_cwNxw5z~FpHK>TF#aBGqktY>g-b+a_RyN? zTE`c{=2hmIeM!3;282`0r9+6*IM{QID?+zxvWR^}XZ%eh;>2i0sN(aUY>6Z3(EzBS zvnsexQ?@Hw9K`em@}4}fkKewpDS|^FvQ9)Fb z?{`oOwSw8pj?wj~`EJ_- zowH4sbLyJ_;fA%21A;H;x2<1p|G3kC!{hzs+Q-#*jScF%3%$sHK%uV~=o|b|f!Led zm>SyJI*LFbR^~>3x^Cvh4A|guVs+ozj9S&hX)AwJeuyD;Z{*%Vxa>z6ZsqZId?vp( z-&mIw8$IXU_m;3;Eb^*C2T+2lF4Cej8oqW230i(8=9d7lNkZFtzT#LltnYJE?jZNpT# zXa^mU)1D%$pyUxb*FTMMD-xj|c zwY&bS`S87c@drSb0@)Z_f$hIt(V8@@A*vcg-h=Ba^DK#P){0RB5#Z{jrK<5{D1GxO zv5>5g=qNRdt9cuEQ)Qjc*v{tBb0?TItJE2TvxBuQs|?3s7_BMrn7!3r-`Ml7d!BpHqe+?&?L*pBRebzZCrZ}UNvtBy}8}!ErzOIbU}fE*eM|^fmFik?$j|5y-WZuUhmJ z164TVoZS`^U62j})A-9rfFx2Nuq#8izOPKcv1@k;cTy$2-nw_;qH|=x4U<=VUMC5V zE`kRt4C+jx-Cf86A#itXohA*cF3vF_BF(4ax9^tYcsS`Okd%T{G|VGwn9I0uNP6H_ z%1DHa9mQu}r&*fBgsXS0;mj$QYf~gunX~Tg-M=)Y@RNZDING6r(A6|Wzr3xVS(zr# z6vDrCo}>d}8nnfP5P$q^_ffPZZ=iwub>|#hZlBC&)Hq2dln1u=`iUYM;_o>D%g9LZ zW~m`p#-7A`of6P;F3x=FUFd+=!xX->!Xv4k%ZFPknA8@2gT+Qi^4EKXhsC3 z$8ggtWGZ7WnlV6{}&MKEmjPWwj4P=bN zHCrghy*8W1+750&N23DEg`;yNIlx@O=iR#am_WXZ@Qh(*1*@F-y#7)U7y$bNy3p`lBbT85l5n|qL5AB zQulXrk1Z-md&#t8Z-&O4dL-#yRR(n*68Yj_di(C~Bc8Kt4=o*|WF#4`*N?N7E=hZe z7$oGJ5#^A;$c>Vr&>hO@cXlKB#fEmj@DEvm>Em|~JF@AM-?ph6*Bg{ikgZ$GSFxF@ z2D6*abUvx^Oz2Zog|Z380#m^Bg&- zWH~ql+PSZi_oA?tL2r50w6{Tn8~5U|gVd|WYIgH;D0Q4-XJ*sxa5|NU7pPs4@jS{Y zl4CY>!1|pDm~t5Oxx>Wq_a4fvdj%&AfxXN#K+9ir69CeVjM~Tj*0n)bt5BK)5^8xJ|vp+>Y`W zu}*e3>SnK-k%QLZB^838$aIh6qw=NEjRpS0j)P7WAE9H{I>Gs)CDYPorxRD}y2agv zw58)-kLvdwdi>^?YLN8yp2{|j1O8ACd-e6$p|G|7SgIR#k!O*BhcQx#cAhM2yzgzL z9_pU6E)72EU*591DO%LHVMcw?m*jwrig-@_Z05-CrPP!Tv=3_+jt-BqedfCK{%DKilN*Dw>24DB94B?%PBi=l|-E2sD3ee^W z5P&r+L{anT$S9%|gSQB6?-bRfhY+P&$POAxTAQ!w+ARadgdxc2RJ#1ZB^qV1O8Xd9 zXxUZjS-JwL{tq|Y(Y8#FCW&ffQ1|Pd_2FWW)SGK1nY^X_~Ea-zI@(sdcZ}w;# ziivQ|z}?vy@zSQ+SR)V||4F{$3+|#qFIKX5bFOIHkhuEH4xtBKRV!om2&o6?%~cr^ zq#5ld&Qq+eWM}1W6D#n0O~F@g_t9&EO3u+)n|!U$sY|Y36ds%Iw5AHCfw)(!wQotB zSN3PG5Dm}iwITlO_<#~avIQnCw+urWP68V`%tGArn0^~h|BOr;GD_@4Mf;csEL|-J zV9&swnv@p#rQ-fG?xy2HCii2WG!u(`DukySDQyfb;t{dB0`la8987`pZ(4clibpoI z6&`1~Fu!M<%*e+@XJ{5FkikQO$A(`xy_x8H8h$I0<9A8Ikrg(x&V?LVxM#ALd4Tf# z4VUm)C;w%seF-p=tV%``*~_#C?CF9vv`>HCmNNPBHD9Pf24zcH(IDme(K;` z#y%*heBPWJ5yf;`0If_*<}}=16K1+?#D+Z^-iltNiX44lM|7+a`K-m+-@e9>vA#}y zA4J5KXZlj%CGSh}XI3%q2)p{NddL^X>uq6TWt{wqj;u z=;ycPbwst783i6Y z0%c@SYN5V^yuVLBzk2#(`e_Yv2LE}o_w%Tf{O`g>Hf+BS`q;i+3jQ<=`&s#G&;KV+ zKP&$>0c8C)(qp|F&apy%Yq_$T-CaNau#EKE1Q)r@VCw4b=5p`u0HXhomLay*Y_8T; zCF%natK3-4Ln>m}lT&kVVISu_Q8y##kZCZaG3XdiuWb-YpNigfJbSiYCMk~;(D4G6 z*A<#AxW-qH%wHz0!+3Xv?8)`PCH_X0<94-MQKW$+sLY1p!1W#l;8P zZyts|#K@#GwPWmLm2)c7*ip{2)Q(D|qOIrDw$#%%)Qw_Y3yy<}h>l|RyzXT7^ffWV zA0xJS0)E3q9Y!n;w*Z0)uH}Dc?TX*}X1Eznx2e;nMzn!GeDA{Qe)mG7`vn-5&rsLA zO@)}wfS7D zyvPlEe2ivC?_8|kMDsL8+Bv8*gH`il;+?i*YZh(~7q&AOZmx|E)}LqITx<{Vw%nfX zziEL}8a+_QH=4|5EX1RQUr?Vydd4#7lkLGUmL$uW=n7rab?q}dz{k$k-5|~_&^XZyqU4r`Ka3{?U>SxN7A##Si<2k)J@lJamn8Nv* zi@k?ls_VYgx_u$!>9=HUe3zeI#zCH(ahGgC=f3^`Xi$!7uYG-9^BT?O1zG~jD^8Q? zN3V2o;jbw-gI+JrdR{Uyi`cLAX`90!iwJM|G$WIhzZk%KO~V&}@`?kXu8P?SF;G_I zYC;LecW*HMCP{2bAYf z#^vw#<&B2<+>aixFue~tEhkSG6WeyL>614e={ZTy)@|uT;>#@*l78k`=FJt+FncA8 zR4v~yg|rDPxGYEq7E*d>?)7LWA&LZV=h5COPfDAIBBDua;p;j=qk%(ZoaEAJ=cxlf zNOeH!DPvfZcR6Hm00Xbd%(>a7$se(!H&Rp3vN~_RpssFWFL^ExL(X&qfNpRGCUco>-Wzb&Z6WHJzWrVwztxu8#=u0kyPERs z?jiI4p|(KmZ6S99By+F>dXFqh8wVED2QPWf4^EG9gw3*;39=V^^(o}Khzo#}@+eJB z&AZ#BQkwn-e&+s?n$fDf_X9jA4lriR7c#HrklBW?8&42GPlp-pOPbZ0dXG@?&lug4|hbkzjpWiga6}Hf9d%B?B{1d`@>J%-B;l!c>6>7 zpE2q;Wh|)M+nYP(KPt_4#QGz(e}dx3^~hZR|e~ z?e}f}Gp7CSEZ4W~|IyR$2=~X){`rUQheI-||BTZwkM`B!kDon7S$Kr6w(;)%yzk!K J-dTr&`afenk2e4S diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java index 74c5651017..648b7dda65 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java @@ -1,16 +1,30 @@ package cn.iocoder.yudao.module.iot.plugin.http; +import cn.iocoder.yudao.module.iot.plugin.http.config.VertxService; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +/** + * 独立运行入口 + */ +@Slf4j @SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") public class HttpPluginSpringbootApplication { public static void main(String[] args) { + // 这里可选择 NONE / SERVLET / REACTIVE,看你需求 SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); application.setWebApplicationType(WebApplicationType.NONE); - application.run(args); - } + ConfigurableApplicationContext context = application.run(args); + + // 手动获取 VertxService 并启动 + VertxService vertxService = context.getBean(VertxService.class); + vertxService.startServer(); + + log.info("[HttpPluginSpringbootApplication] 独立模式启动完成"); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index e6145b93d5..85249cdb85 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -2,80 +2,70 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; -import io.vertx.core.Vertx; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.handler.BodyHandler; import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPlugin; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +/** + * 负责插件的启动和停止,与 Vert.x 的生命周期管理 + */ @Slf4j public class HttpVertxPlugin extends SpringPlugin { - private static final int PORT = 8092; - private Vertx vertx; - public HttpVertxPlugin(PluginWrapper wrapper) { super(wrapper); } @Override public void start() { - log.info("HttpVertxPlugin.start()"); + log.info("[HttpVertxPlugin] start ..."); - // 获取 DeviceDataApi 实例 - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); - if (deviceDataApi == null) { - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); + // 1. 获取插件上下文 + ApplicationContext pluginContext = getApplicationContext(); + if (pluginContext == null) { + log.error("[HttpVertxPlugin] pluginContext is null, start failed."); return; } - // 初始化 Vert.x - vertx = Vertx.vertx(); - Router router = Router.router(vertx); - - // 处理 Body - router.route().handler(BodyHandler.create()); - - // 设置路由 - router.post("/sys/:productKey/:deviceName/thing/event/property/post") - .handler(new HttpVertxHandler(deviceDataApi)); - - // 启动 HTTP 服务器 - vertx.createHttpServer() - .requestHandler(router) - .listen(PORT, http -> { - if (http.succeeded()) { - log.info("HTTP 服务器启动成功,端口为: {}", PORT); - } else { - log.error("HTTP 服务器启动失败", http.cause()); - } - }); + // 2. 启动 Vertx + VertxService vertxService = pluginContext.getBean(VertxService.class); + vertxService.startServer(); } + @Override public void stop() { - log.info("HttpVertxPlugin.stop()"); - if (vertx != null) { - vertx.close(ar -> { - if (ar.succeeded()) { - log.info("Vert.x 关闭成功"); - } else { - log.error("Vert.x 关闭失败", ar.cause()); - } - }); + log.info("[HttpVertxPlugin] stop ..."); + ApplicationContext pluginContext = getApplicationContext(); + if (pluginContext != null) { + // 停止服务器 + VertxService vertxService = pluginContext.getBean(VertxService.class); + vertxService.stopServer(); } } @Override protected ApplicationContext createApplicationContext() { - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); - applicationContext.refresh(); + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext() { + @Override + protected void prepareRefresh() { + // 在刷新容器前注册主程序中的 Bean + ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); + DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + beanFactory.registerSingleton("deviceDataApi", deviceDataApi); - return applicationContext; + super.prepareRefresh(); + } + }; + + pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); + pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); + pluginContext.refresh(); + + return pluginContext; } -} \ No newline at end of file + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java index b5e977efbb..55bce6f24a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -1,16 +1,67 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; -import org.pf4j.DefaultPluginManager; -import org.pf4j.PluginWrapper; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * 插件与独立运行都可复用的配置类 + */ +@Slf4j @Configuration public class HttpVertxPluginConfiguration { - @Bean(initMethod = "start") - public HttpVertxPlugin httpVertxPlugin() { - PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null); - return new HttpVertxPlugin(pluginWrapper); + /** + * 可在 application.yml 中配置,默认端口 8092 + */ + @Value("${plugin.http.server.port:8092}") + private Integer port; + + /** + * 创建 Vert.x 实例 + */ + @Bean + public Vertx vertx() { + return Vertx.vertx(); } -} \ No newline at end of file + + /** + * 创建路由 + */ + @Bean + public Router router(Vertx vertx, HttpVertxHandler httpVertxHandler) { + Router router = Router.router(vertx); + + // 处理 Body + router.route().handler(BodyHandler.create()); + + // 设置路由 + router.post("/sys/:productKey/:deviceName/thing/event/property/post") + .handler(httpVertxHandler); + + return router; + } + + /** + * 注入你的 Http 处理器 Handler,依赖 DeviceDataApi + */ + @Bean + public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { + return new HttpVertxHandler(deviceDataApi); + } + + /** + * 定义一个 VertxService 来管理服务器启动逻辑 + * 无论是独立运行还是插件方式,都可以共用此类 + */ + @Bean + public VertxService vertxService(Vertx vertx, Router router) { + return new VertxService(port, vertx, router); + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java new file mode 100644 index 0000000000..5a57be8eee --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.plugin.http.config; + +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; +import lombok.extern.slf4j.Slf4j; + +/** + * 封装 Vert.x HTTP 服务的启动/关闭逻辑 + */ +@Slf4j +public class VertxService { + + private final Integer port; + private final Vertx vertx; + private final Router router; + + public VertxService(Integer port, Vertx vertx, Router router) { + this.port = port; + this.vertx = vertx; + this.router = router; + } + + /** + * 启动 HTTP 服务器 + */ + public void startServer() { + vertx.createHttpServer() + .requestHandler(router) + .listen(port, http -> { + if (http.succeeded()) { + log.info("[VertxService] HTTP 服务器启动成功, 端口: {}", port); + } else { + log.error("[VertxService] HTTP 服务器启动失败", http.cause()); + } + }); + } + + /** + * 关闭 HTTP 服务器 + */ + public void stopServer() { + if (vertx != null) { + vertx.close(ar -> { + if (ar.succeeded()) { + log.info("[VertxService] Vert.x 关闭成功"); + } else { + log.error("[VertxService] Vert.x 关闭失败", ar.cause()); + } + }); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java index becba2a082..df55c68fd6 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java @@ -22,77 +22,51 @@ public class HttpVertxHandler implements Handler { public void handle(RoutingContext ctx) { String productKey = ctx.pathParam("productKey"); String deviceName = ctx.pathParam("deviceName"); - RequestBody requestBody = ctx.body(); + RequestBody requestBody = ctx.body(); JSONObject jsonData; try { jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); } catch (Exception e) { - JSONObject res = createResponseJson( - 400, - new JSONObject(), - null, - "请求数据不是合法的 JSON 格式: " + e.getMessage(), - "thing.event.property.post", - "1.0"); - ctx.response() - .setStatusCode(400) + log.error("[HttpVertxHandler] 请求数据解析失败", e); + ctx.response().setStatusCode(400) .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(res.toString()); + .end(createResponseJson(400, null, null, + "请求数据不是合法的 JSON 格式: " + e.getMessage(), + "thing.event.property.post", "1.0").toString()); return; } - String id = jsonData.getStr("id", null); + String id = jsonData.getStr("id"); try { - // 调用主程序的接口保存数据 - IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() + IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder() .productKey(productKey) .deviceName(deviceName) - .params(jsonData) // TODO 芋艿:这块要优化 + .params(jsonData) .build(); - deviceDataApi.reportDevicePropertyData(createDTO); - // 构造成功响应内容 - JSONObject successRes = createResponseJson( - 200, - new JSONObject(), - id, - "success", - "thing.event.property.post", - "1.0"); + deviceDataApi.reportDevicePropertyData(reportReqDTO); + ctx.response() .setStatusCode(200) .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(successRes.toString()); + .end(createResponseJson(200, new JSONObject(), id, "success", + "thing.event.property.post", "1.0").toString()); + } catch (Exception e) { - JSONObject errorRes = createResponseJson( - 500, - new JSONObject(), - id, - "The format of result is error!", - "thing.event.property.post", - "1.0"); + log.error("[HttpVertxHandler] 上报属性数据失败", e); ctx.response() .setStatusCode(500) .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(errorRes.toString()); + .end(createResponseJson(500, new JSONObject(), id, + "The format of result is error!", + "thing.event.property.post", "1.0").toString()); } } - /** - * 创建标准化的响应 JSON 对象 - * - * @param code 响应状态码(业务层面的) - * @param data 返回的数据对象(JSON) - * @param id 请求的 id(可选) - * @param message 返回的提示信息 - * @param method 返回的 method 标识 - * @param version 返回的版本号 - * @return 构造好的 JSON 对象 - */ - private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, - String version) { + private JSONObject createResponseJson(int code, JSONObject data, String id, + String message, String method, String version) { JSONObject res = new JSONObject(); res.set("code", code); res.set("data", data != null ? data : new JSONObject()); From 534c64709fc6c9d6053f1f898d62438877ab4bef Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 Jan 2025 09:49:25 +0800 Subject: [PATCH 098/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91Bpm=EF=BC=9A=E8=A7=A6=E5=8F=91=E5=99=A8?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/listener/BpmUserTaskListener.java | 1 + .../yudao/module/bpm/service/task/trigger/BpmTrigger.java | 1 + 2 files changed, 2 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java index e89587d471..97c103c143 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -26,6 +26,7 @@ import java.util.Map; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; +// TODO @芋艿:可能会想换个包地址 /** * BPM 用户任务通用监听器 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java index 8ebd9735e9..dfbaa63ecb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmTrigger.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger; import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +// TODO @芋艿:可能会想换个包地址 /** * BPM 触发器接口 *

From 06634a426584d663a58c57b35c9aaffbd9757810 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 Jan 2025 10:09:39 +0800 Subject: [PATCH 099/309] =?UTF-8?q?=E3=80=90=E5=8D=95=E6=B5=8B=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91system=EF=BC=9AAdminAuthServiceImplTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/auth/AdminAuthServiceImpl.java | 2 ++ .../auth/AdminAuthServiceImplTest.java | 27 +++---------------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java index e81c75724c..4b09980235 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -30,6 +30,7 @@ import com.xingyuv.captcha.model.vo.CaptchaVO; import com.xingyuv.captcha.service.CaptchaService; import jakarta.annotation.Resource; import jakarta.validation.Validator; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -71,6 +72,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { * 验证码的开关,默认为 true */ @Value("${yudao.captcha.enable:true}") + @Setter // 为了单测:开启或者关闭验证码 private Boolean captchaEnable; @Override diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java index 2f5f717e24..151150bc5a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -22,7 +22,6 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService; import com.xingyuv.captcha.model.common.ResponseModel; import com.xingyuv.captcha.service.CaptchaService; import jakarta.annotation.Resource; -import jakarta.validation.ConstraintViolationException; import jakarta.validation.Validation; import jakarta.validation.Validator; import org.junit.jupiter.api.BeforeEach; @@ -37,7 +36,6 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @@ -66,7 +64,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { @BeforeEach public void setUp() { - ReflectUtil.setFieldValue(authService, "captchaEnable", true); + authService.setCaptchaEnable(true); // 注入一个 Validator 对象 ReflectUtil.setFieldValue(authService, "validator", Validation.buildDefaultValidatorFactory().getValidator()); @@ -156,7 +154,7 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { .setSocialType(randomEle(SocialTypeEnum.values()).getType())); // mock 验证码正确 - ReflectUtil.setFieldValue(authService, "captchaEnable", false); + authService.setCaptchaEnable(false); // mock user 数据 AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username") .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus())); @@ -269,8 +267,6 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { // 准备参数 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); - // mock 验证码打开 - ReflectUtil.setFieldValue(authService, "captchaEnable", true); // mock 验证通过 when(captchaService.verification(argThat(captchaVO -> { assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); @@ -287,34 +283,17 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest { AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); // mock 验证码关闭 - ReflectUtil.setFieldValue(authService, "captchaEnable", false); + authService.setCaptchaEnable(false); // 调用,无需断言 authService.validateCaptcha(reqVO); } - @Test - public void testValidateCaptcha_constraintViolationException() { - // 准备参数 - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); - reqVO.setCaptchaVerification(null); - - // mock 验证码打开 - ReflectUtil.setFieldValue(authService, "captchaEnable", true); - - // 调用,并断言异常 - assertThrows(ConstraintViolationException.class, () -> authService.validateCaptcha(reqVO), - "验证码不能为空"); - } - - @Test public void testCaptcha_fail() { // 准备参数 AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); - // mock 验证码打开 - ReflectUtil.setFieldValue(authService, "captchaEnable", true); // mock 验证通过 when(captchaService.verification(argThat(captchaVO -> { assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); From 69efe91bdeb40ba5cee68fab57b850637fd06ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E9=87=8E=E7=BE=A1=E6=B0=91?= Date: Sat, 25 Jan 2025 02:47:03 +0000 Subject: [PATCH 100/309] =?UTF-8?q?=E9=9B=AA=E8=8A=B1ID=E6=BA=A2=E5=87=BA?= =?UTF-8?q?=E4=BA=86=20update=20=20apierrorlog/ApiErrorLogRespVO.java.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 山野羡民 --- .../admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java index 53f52f02c5..8029f1584e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java @@ -17,7 +17,7 @@ public class ApiErrorLogRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @ExcelProperty("编号") - private Integer id; + private Long id; @Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "66600cb6-7852-11eb-9439-0242ac130002") @ExcelProperty("链路追踪编号") @@ -25,7 +25,7 @@ public class ApiErrorLogRespVO { @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") @ExcelProperty("用户编号") - private Integer userId; + private Long userId; @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @ExcelProperty(value = "用户类型", converter = DictConvert.class) From 5264de077d71f2a24414caf75497d6519cdf9e8f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 Jan 2025 11:26:10 +0800 Subject: [PATCH 101/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=8F=92=E4=BB=B6=E4=BD=93?= =?UTF-8?q?=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-module-iot-plugin-common/pom.xml | 1 + .../iot/plugin/common/api/DeviceDataApiClient.java | 9 ++++++--- .../plugin/common/config/DeviceDataApiInitializer.java | 6 ++++-- .../yudao/module/iot/plugin/common/package-info.java | 1 + .../iot/plugin/http/HttpPluginSpringbootApplication.java | 6 +++--- .../module/iot/plugin/http/config/HttpVertxPlugin.java | 5 +++-- .../plugin/http/config/HttpVertxPluginConfiguration.java | 6 +++--- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml index 568f862004..676b25f9ef 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml @@ -13,6 +13,7 @@ ${project.artifactId} + 物联网 插件 模块 - 通用功能 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java index 183c76e58d..f63267b27b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java @@ -10,6 +10,7 @@ import org.springframework.web.client.RestTemplate; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +// TODO @haohao:类注释,写一下,比较好 @Slf4j public class DeviceDataApiClient implements DeviceDataApi { @@ -17,32 +18,32 @@ public class DeviceDataApiClient implements DeviceDataApi { private final String deviceDataUrl; // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 + // TODO @haohao:可以用 lombok 简化 public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) { this.restTemplate = restTemplate; this.deviceDataUrl = deviceDataUrl; } + // TODO @haohao:返回结果,不用 CommonResult 哈。 @Override public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { - // 示例:如果对应的远程地址是 /rpc-api/iot/device-data/update-status String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status"; return doPost(url, updateReqDTO, "updateDeviceStatus"); } @Override public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { - // 示例:如果对应的远程地址是 /rpc-api/iot/device-data/report-event String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event"; return doPost(url, reportReqDTO, "reportDeviceEventData"); } @Override public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - // 示例:如果对应的远程地址是 /rpc-api/iot/device-data/report-property String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property"; return doPost(url, reportReqDTO, "reportDevicePropertyData"); } + // TODO @haohao:未来可能有 get 类型哈 /** * 将与远程服务交互的通用逻辑抽取成一个私有方法 */ @@ -51,10 +52,12 @@ public class DeviceDataApiClient implements DeviceDataApi { try { // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 restTemplate.postForObject(url, requestBody, CommonResult.class); + // TODO @haohao:check 结果,是否成功 return success(true); } catch (Exception e) { log.error("[{}] Error sending request to URL: {}", actionName, url, e); return CommonResult.error(400, "Request error: " + e.getMessage()); } } + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java index 9473033c20..ed39449306 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java @@ -8,21 +8,23 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; +// TODO @haohao:这个最好是 autoconfiguration @Configuration public class DeviceDataApiInitializer { + // TODO @haohao:这个要不搞个配置类哈 @Value("${iot.device-data.url}") private String deviceDataUrl; @Bean public RestTemplate restTemplate() { - // 如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 + // TODO haohao:如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 return new RestTemplateBuilder().build(); } + // TODO @haohao:不存在时,才构建 @Bean public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { - // 返回我们自定义的 Client 实例 return new DeviceDataApiClient(restTemplate, deviceDataUrl); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java index f9eae496d4..83b5bb58aa 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/package-info.java @@ -1 +1,2 @@ +// TODO @芋艿:注释 package cn.iocoder.yudao.module.iot.plugin.common; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java index 648b7dda65..91be33097d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java @@ -11,20 +11,20 @@ import org.springframework.context.ConfigurableApplicationContext; * 独立运行入口 */ @Slf4j -@SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") +@SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") // TODO @haohao:建议不扫描 cn.iocoder.yudao.module.iot.plugin;而是通过自动配置,初始化 common 的 public class HttpPluginSpringbootApplication { public static void main(String[] args) { - // 这里可选择 NONE / SERVLET / REACTIVE,看你需求 SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); application.setWebApplicationType(WebApplicationType.NONE); - ConfigurableApplicationContext context = application.run(args); // 手动获取 VertxService 并启动 + // TODO @haohao:可以放在 bean 的 init 里么? VertxService vertxService = context.getBean(VertxService.class); vertxService.startServer(); log.info("[HttpPluginSpringbootApplication] 独立模式启动完成"); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index 85249cdb85..40694cf40c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -21,7 +21,8 @@ public class HttpVertxPlugin extends SpringPlugin { @Override public void start() { - log.info("[HttpVertxPlugin] start ..."); + // TODO @haohao:这种最好启动中,启动完成,成对打印日志,方便定位问题 + log.info("[HttpVertxPlugin][start ...]"); // 1. 获取插件上下文 ApplicationContext pluginContext = getApplicationContext(); @@ -38,7 +39,7 @@ public class HttpVertxPlugin extends SpringPlugin { @Override public void stop() { - log.info("[HttpVertxPlugin] stop ..."); + log.info("[HttpVertxPlugin][stop ...]"); ApplicationContext pluginContext = getApplicationContext(); if (pluginContext != null) { // 停止服务器 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java index 55bce6f24a..5c221e795a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -17,6 +17,7 @@ import org.springframework.context.annotation.Configuration; @Configuration public class HttpVertxPluginConfiguration { + // TODO @haohao:这个要不要搞个配置类,更容易维护; /** * 可在 application.yml 中配置,默认端口 8092 */ @@ -42,15 +43,13 @@ public class HttpVertxPluginConfiguration { router.route().handler(BodyHandler.create()); // 设置路由 + // TODO @haohao:这个后续,我们是多个 Handler ,还是一个哈? router.post("/sys/:productKey/:deviceName/thing/event/property/post") .handler(httpVertxHandler); return router; } - /** - * 注入你的 Http 处理器 Handler,依赖 DeviceDataApi - */ @Bean public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { return new HttpVertxHandler(deviceDataApi); @@ -64,4 +63,5 @@ public class HttpVertxPluginConfiguration { public VertxService vertxService(Vertx vertx, Router router) { return new VertxService(port, vertx, router); } + } From 269dec1b2e04552dfc5ad7820a81a174e1ccfef2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 Jan 2025 11:46:26 +0800 Subject: [PATCH 102/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=A8=A1=E6=8B=9F=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceDataController.java | 5 +- .../IotDeviceDataSimulatorSaveReqVO.java | 2 +- .../dal/dataobject/device/IotDeviceLogDO.java | 2 - .../dal/tdengine/IotDeviceLogDataMapper.java | 17 ++--- .../TDengineTableInitConfiguration.java | 6 +- .../deviceconsumer/DeviceConsumer.java | 6 +- .../simulatesend/SimulateSendProducer.java | 3 +- .../device/IotDeviceLogDataService.java | 6 +- .../device/IotDeviceLogDataServiceImpl.java | 70 ++++++------------- .../device/IotDevicePropertyDataService.java | 2 - .../IotDevicePropertyDataServiceImpl.java | 4 +- .../mapper/device/IotDeviceLogDataMapper.xml | 27 +++---- .../src/main/resources/application-local.yaml | 58 +++++---------- 13 files changed, 66 insertions(+), 142 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index f78a96b248..638c880467 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; @@ -52,7 +51,7 @@ public class IotDeviceDataController { return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); } - // TODO:数据权限 + // TODO:功能权限 @PostMapping("/simulator") @Operation(summary = "模拟设备") public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { @@ -62,7 +61,7 @@ public class IotDeviceDataController { return success(true); } - // TODO:数据权限 + // TODO:功能权限 @GetMapping("/log/page") @Operation(summary = "获得设备日志分页") public CommonResult> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java index 4d09808b14..efa608e572 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java @@ -19,7 +19,6 @@ public class IotDeviceDataSimulatorSaveReqVO { @NotEmpty(message = "产品标识不能为空") private String productKey; - // TODO @super:中文写作规范,中英文之间,要有空格。例如说,设备 ID。ps:这里应该是设备标识 @Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") @NotEmpty(message = "设备标识不能为空") private String deviceKey; @@ -38,6 +37,7 @@ public class IotDeviceDataSimulatorSaveReqVO { @NotEmpty(message = "数据内容不能为空") private String content; + // TODO @芋艿:需要讨论下,reportTime 到底以那个为准! @Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED) private Long reportTime; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java index 609c9c43ad..158b0f57b9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java @@ -25,7 +25,6 @@ public class IotDeviceLogDO { */ private String id; - // TODO @super:关联要 @下 /** * 产品标识 *

@@ -33,7 +32,6 @@ public class IotDeviceLogDO { */ private String productKey; - // TODO @super:关联要 @下 /** * 设备标识 *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index 2d8230f1f6..f9d18bdccd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -10,9 +10,7 @@ import org.apache.ibatis.annotations.Param; import java.util.List; /** - * IOT 设备日志数据 Mapper 接口 - * - * 基于 TDengine 实现设备日志的存储 + * 设备日志 {@link IotDeviceLogDO} Mapper 接口 */ @Mapper @TDengineDS @@ -21,12 +19,11 @@ public interface IotDeviceLogDataMapper { /** * 创建设备日志超级表 - * 初始化只创建一次 */ void createDeviceLogSTable(); - // TODO @super:单个参数,不用加 @Param + // TODO @芋艿:在瞅瞅 //讨论:艿菇这里有些特殊情况,我也学习了一下这块知识: // 如果使用的是Java 8及以上版本,并且编译器保留了参数名(通过编译器选项-parameters启用),则可以去掉@Param注解。MyBatis会自动使用参数的实际名称 // 但在TDengine中 @Param去掉后TDengine会报错,以下是大模型的回答: @@ -65,18 +62,12 @@ public interface IotDeviceLogDataMapper { */ Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO); + // TODO @芋艿:这个方法名,后续看看叫啥好 /** * 查询设备日志表是否存在 * - * @return 不存在返回null + * @return 不存在返回 null */ Object checkDeviceLogSTableExists(); - /** - * 检查设备日志子表是否存在 - * - * @param deviceKey 设备标识 - * @return 不存在返回null - */ - Object checkDeviceLogTableExists(@Param("deviceKey") String deviceKey); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java index c9f08903e8..9f3c79c227 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java @@ -6,17 +6,15 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; /** * TDengine 表初始化的 Configuration * * @author alwayssuper */ +@Configuration @Slf4j @RequiredArgsConstructor -@Configuration -@Order public class TDengineTableInitConfiguration implements ApplicationRunner { private final IotDeviceLogDataService deviceLogService; @@ -26,9 +24,7 @@ public class TDengineTableInitConfiguration implements ApplicationRunner { try { // 初始化设备日志表 deviceLogService.defineDeviceLog(); - // TODO @super:这个日志,是不是不用打,不然重复啦!!! } catch (Exception ex) { - // TODO @super:初始化失败,打印 error 日志,退出系统。。不然跑起来,就初始啦!!! // 初始化失败时打印错误日志并退出系统 log.error("[TDengine] 初始化设备日志表结构失败,系统无法正常运行,即将退出", ex); System.exit(1); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java index 1daa05f920..7403fb6686 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.mq.consumer.deviceconsumer; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; @@ -20,8 +19,9 @@ import javax.annotation.Resource; @Component @Slf4j public class DeviceConsumer { + @Resource - private IotDeviceLogDataService iotDeviceLogDataService; + private IotDeviceLogDataService deviceLogDataService; @Resource private IotDevicePropertyDataService deviceDataService; @@ -35,7 +35,7 @@ public class DeviceConsumer { // 设备数据记录 deviceDataService.saveDeviceDataTest(message); // 设备日志记录 - iotDeviceLogDataService.saveDeviceLog(message); + deviceLogDataService.saveDeviceLog(message); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java index 1fb0c80c00..7366f4da54 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java @@ -6,7 +6,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; -// TODO @alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块; +// TODO @芋艿:@alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块; /** * SimulateSend 模拟设备上报的 Producer * @@ -28,4 +28,5 @@ public class SimulateSendProducer { public void sendSimulateMessage(ThingModelMessage thingModelMessage) { applicationContext.publishEvent(thingModelMessage); } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java index 2d7bf98500..637c8f51a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java @@ -16,7 +16,7 @@ public interface IotDeviceLogDataService { /** * 初始化 TDengine 超级表 * - *系统启动时,会自动初始化一次 + * 系统启动时,会自动初始化一次 */ void defineDeviceLog(); @@ -40,8 +40,8 @@ public interface IotDeviceLogDataService { /** * 插入设备日志 * - * @param msg 设备数据 + * @param message 设备数据 */ - void saveDeviceLog(ThingModelMessage msg); + void saveDeviceLog(ThingModelMessage message); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java index f66bcc8ad2..bfe3199551 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java @@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.util.Date; import java.util.List; /** @@ -27,28 +26,18 @@ import java.util.List; public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ @Resource - private IotDeviceLogDataMapper iotDeviceLogDataMapper; + private IotDeviceLogDataMapper deviceLogDataMapper; - // TODO @super:方法名。defineDeviceLog。。未来,有可能别人使用别的记录日志,例如说 es 之类的。 @Override public void defineDeviceLog() { - // TODO @super:改成不存在才创建。 -// try { -// // 创建超级表(使用 IF NOT EXISTS 语句避免重复创建错误) -// iotDeviceLogDataMapper.createDeviceLogSTable(); -// } catch (Exception e) { -// if (e.getMessage().contains("already exists")) { -// log.info("[TDengine] 设备日志超级表已存在,跳过创建"); -// return; -// } -// throw e; -// } - if(iotDeviceLogDataMapper.checkDeviceLogSTableExists()==null){ - log.info("[TDengine] 设备日志超级表不存在,开始创建"); - iotDeviceLogDataMapper.createDeviceLogSTable(); - }else{ - log.info("[TDengine] 设备日志超级表已存在,跳过创建"); + if (deviceLogDataMapper.checkDeviceLogSTableExists() != null) { + log.info("[defineDeviceLog][设备日志超级表已存在,跳过创建]"); + return; } + + log.info("[defineDeviceLog][设备日志超级表不存在,开始创建]"); + deviceLogDataMapper.createDeviceLogSTable(); + log.info("[defineDeviceLog][设备日志超级表不存在,创建完成]"); } @Override @@ -57,47 +46,32 @@ public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class); // 2. 处理时间字段 - // TODO @super:一次性的字段,不用单独给个变量 -// long currentTime = System.currentTimeMillis(); - // 2.1 设置时序时间为当前时间 // iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now() // 3. 插入数据 - // TODO @super:不要直接调用对方的 IotDeviceLogDataMapper,通过 service 哈! - // 讨论:艿菇 这就是iotDeviceLogDataService的Impl - iotDeviceLogDataMapper.insert(iotDeviceLogDO); + deviceLogDataMapper.insert(iotDeviceLogDO); } - // TODO @super:在 iotDeviceLogDataService 写 - // 讨论:艿菇 这就是iotDeviceLogDataService的Impl @Override public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { - // 当设备日志表未创建时,查询会出现报错 - if(iotDeviceLogDataMapper.checkDeviceLogTableExists(pageReqVO.getDeviceKey())==null){ - return null; - } - // 查询数据 - List list = iotDeviceLogDataMapper.selectPage(pageReqVO); - Long total = iotDeviceLogDataMapper.selectCount(pageReqVO); - // 构造分页结果 + // TODO @芋艿:增加一个表不存在的 try catch + List list = deviceLogDataMapper.selectPage(pageReqVO); + Long total = deviceLogDataMapper.selectCount(pageReqVO); return new PageResult<>(list, total); } @Override - public void saveDeviceLog(ThingModelMessage msg) { - // 1. 构建设备日志对象 - IotDeviceLogDO iotDeviceLogDO = IotDeviceLogDO.builder() - .id(msg.getId()) // 消息ID - .deviceKey(msg.getDeviceKey()) // 设备标识 - .productKey(msg.getProductKey()) // 产品标识 - .type(msg.getMethod()) // 消息类型,使用method作为类型 - .subType("property") // TODO:这块先写死,后续优化 - .content(JSONUtil.toJsonStr(msg)) // TODO:后续优化 - .reportTime(msg.getTime()) // 上报时间 + public void saveDeviceLog(ThingModelMessage message) { + IotDeviceLogDO log = IotDeviceLogDO.builder() + .id(message.getId()) + .deviceKey(message.getDeviceKey()) + .productKey(message.getProductKey()) + .type(message.getMethod()) // 消息类型,使用method作为类型 TODO 芋艿:在看看 + .subType("property") // TODO 芋艿:这块先写死,后续优化 + .content(JSONUtil.toJsonStr(message)) // TODO 芋艿:后续优化 + .reportTime(message.getTime()) // 上报时间 TODO 芋艿:在想想时间 .build(); - - // 2. 插入设备日志 - iotDeviceLogDataMapper.insert(iotDeviceLogDO); + deviceLogDataMapper.insert(log); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java index 92a64c28f8..672785694e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java @@ -32,7 +32,6 @@ public interface IotDevicePropertyDataService { */ void saveDeviceData(IotDevicePropertyReportReqDTO createDTO); - /** * 保存设备数据 * @@ -45,7 +44,6 @@ public interface IotDevicePropertyDataService { * * @param simulatorReqVO 设备数据 */ - void simulatorSend(IotDeviceDataSimulatorSaveReqVO simulatorReqVO); /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java index 2b998e7886..abd788948f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java @@ -159,7 +159,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } - //TODO:后续捋一捋这块逻辑,先借鉴一下目前的代码 + //TODO @芋艿:后续捋一捋这块逻辑,先借鉴一下目前的代码 @Override public void saveDeviceDataTest(ThingModelMessage thingModelMessage) { // 1. 根据产品 key 和设备名称,获得设备信息 @@ -168,7 +168,7 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } - //TODO: copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 + //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 @Override public void simulatorSend(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { // 1. 根据设备 key ,获得设备信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 828522ed70..039180857b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -4,28 +4,20 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - CREATE STABLE IF NOT EXISTS device_log ( - ts TIMESTAMP, - id NCHAR(50), - product_key NCHAR(50), - type NCHAR(50), - - sub_type NCHAR(50), - content NCHAR(1024), - report_time TIMESTAMP + ts TIMESTAMP, + id NCHAR(50), + product_key NCHAR(50), + type NCHAR(50), + sub_type NCHAR(50), + content NCHAR(1024), + report_time TIMESTAMP ) TAGS ( device_key NCHAR(50) ) - - - CREATE TABLE device_log_${deviceKey} USING device_log TAGS('${deviceKey}') - - - INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time) USING device_log @@ -42,7 +34,7 @@ - - - \ No newline at end of file diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 08918a667b..4a23c0d577 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,7 +45,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 @@ -53,8 +53,8 @@ spring: # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: ruoyi-vue-pro - password: ruoyi-@h2ju02hebp + username: root + password: ahh@123456 # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -63,25 +63,17 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true - username: ruoyi-vue-pro - password: ruoyi-@h2ju02hebp - tdengine: # IOT 数据库 -# lazy: true # 开启懒加载,保证启动速度 - url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro - driver-class-name: com.taosdata.jdbc.rs.RestfulDriver + url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root - password: taosdata - druid: - validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL + password: ahh@123456 # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: chaojiniu.top # 地址 + host: 127.0.0.1 # 地址 port: 6379 # 端口 - database: 15 # 数据库索引 - password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 + database: 0 # 数据库索引 +# password: dev # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### @@ -191,12 +183,12 @@ debug: false --- #################### 微信公众号、小程序相关配置 #################### wx: mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 -# app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) -# secret: 5abee519483bc9f8cb37ce280e814bd0 + # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) + # secret: 5abee519483bc9f8cb37ce280e814bd0 app-id: wx5b23ba7a5589ecbb # 测试号(自己的) secret: 2a7b3b20c537e52e74afd395eb85f61f -# app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) -# secret: bd4f9fab889591b62aeac0d7b8d8b4a0 + # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) + # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 # 存储配置,解决 AccessToken 的跨节点的共享 config-storage: type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 @@ -205,10 +197,10 @@ wx: miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) # secret: 333ae72f41552af1e998fe1f54e1584a -# appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 -# secret: 6f270509224a7ae1296bbf1c8cb97aed -# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) -# secret: 4a1a04e07f6a4a0751b39c3064a92c8b + # appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 + # secret: 6f270509224a7ae1296bbf1c8cb97aed + # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) + # secret: 4a1a04e07f6a4a0751b39c3064a92c8b appid: wx66186af0759f47c9 # 测试号(puhui 提供的) secret: 3218bcbd112cbc614c7264ceb20144ac config-storage: @@ -267,7 +259,7 @@ justauth: iot: emq: # 账号 - username: haohao + username: anhaohao # 密码 password: ahh@123456 # 主机地址 @@ -279,18 +271,4 @@ iot: # 保持连接 keepalive: 60 # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) - clearSession: true - -# MQTT-RPC 配置 -mqtt: - broker: tcp://chaojiniu.top:1883 - username: haohao - password: ahh@123456 - clientId: mqtt-rpc-server-${random.int} - requestTopic: rpc/request - responseTopicPrefix: rpc/response/ - - -# 插件配置 -pf4j: - pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录 \ No newline at end of file + clearSession: true \ No newline at end of file From 7bfa83062859d0e32e58cae406d60b66f7593e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 26 Jan 2025 17:29:03 +0800 Subject: [PATCH 103/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E9=87=8D=E6=9E=84=20HTTP=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-module-iot-plugin-http-1.0.0.jar | Bin 12284 -> 12522 bytes .../plugin/PluginInstanceServiceImpl.java | 90 +++++++++--------- .../common/api/DeviceDataApiClient.java | 68 ++++++++++--- .../config/DeviceDataApiInitializer.java | 31 ------ .../YudaoDeviceDataApiAutoConfiguration.java | 51 ++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + .../http/HttpPluginSpringbootApplication.java | 3 +- .../plugin/http/config/HttpVertxPlugin.java | 50 ++++++---- .../config/HttpVertxPluginConfiguration.java | 14 +++ .../src/main/resources/application.yml | 5 + 10 files changed, 204 insertions(+), 109 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/plugins/yudao-module-iot-plugin-http-1.0.0.jar b/plugins/yudao-module-iot-plugin-http-1.0.0.jar index d3c4facae3d94154105193a3a07734640566e65f..c504342cf851e2a8ca4ad345e8f7f090f795960e 100644 GIT binary patch delta 4924 zcmaJ_2QZvp_g=m2FM2GZTYc5VMvtDDjRimL9psZ4^~Okh!QPCiB7bri55hU z2+>P|NQC&a-F|N2ca4s>j`h-k%&l# z_QGyqt3*`0PXqwCQ2+o2I0H?B&Y(CnG%Y~!h=C;qnjbbn`Z+3c zJk*Jm9en%RL)Fb&N`v2Qu#dhhpdvW^CSS~aF-o^^@QI3;YSS}n?}&nNEG{P%6M*nIaler`u>YWEeASyhyu z(PDalzz!QwHMojGzwJ}{A@PV)T7JV+lHFHzz8+4+(DKHsolnEKfVi2`1pk5`F3D>T zYRV!t^V^CgnW#ye!<4sJA{)_5Qt@;-fGDKG;y~_O<-P>+-hbwMAlLzK6!<*^*7ez? z=JrSDb@h>Nk)_F>k^Nkv3~N;LF-w(U6y`w#;w*X(xe$RIcJ0Ayd--y^MI1O_+v*8S?XDCZ;^cGc{BO)+ua&rhfo ztE&{gZC{fQcf$7JfF%4F5S`X0Hrv50_MT|Cl(3AuYrH$LZMV@E=Qn!%UbG(ALx(1_ zamAN$3GK7)`?0p{)ZG4#HUxL2wu5*lHY)uhFk79@lTvAr1)#rnyQwjWSoD zU{FEBc{GxT-IhjwP1)ewzaif#P%|KtQqd`e8 zUAK|Tif`Xxu+W2+dlqZ!)nUdJV?USrxx4oRc(7Ryj7?`8s&}SbKMu>=PWl#6B0jk= zX{H~1W5jUZxj{ik;?*?fGY|z6f!Z4RNU{4*j8zpK$F4*z|M^J8+jJh zF4B1?^xxJJJ6&pi`CLqjo9SAxPqZ>)881~!F`t%BYIP@q70@k~Cue@1r_i0ID$EyQ z;KHnc^?F$6yZu$ioAQbGv-huUufMYttrsiL1axAXmh0$ChBgaZ+~X_1?7YZ3;LjQNk~L&dOgx zK4_2F&`L`NqpaMKNTXY+1V<(q&D*suZ=y7jOhYh&?~=n!ojpJZ|Uqj0r-`&EsrZ`Q4Gajb3s0 zL7&+P{l|v22T3qxYUkqvL9Hu4sQ(lcasZs>;^<>(@@Iny0RRG$iwg7S=%Em}*ZhuOJ&K=P_B@45nL7O*Ce=Y7 zpi2?hxiGs^nj2DecjWNxF~{9@LjTxFfHD9K6t;8v`SWg~pU5bG>@w@t`1C!BjxfvTlsNd&1G*DO|UZI7wgK?;K< z2KRs59pYAfB)d|*cC&p(I;$%>VN}NsD2oH#_n@K&O7)EGf{KFljvPHjAAM;4lJ+$S zyAmJ>JZRd?hh3d#VnBS?U`YF>n7ekQwvmaTybr>%w3(AhM73zodwX!a2d16Bq5<+) z)c1?|CiDk6F$~;UPcxU2(Zy`k+-kWi_*Tx}CX1WinVAxDx4$^uAS*w+1wFS)4O(^x zmjar$sqFeDZX#aSfyM3b7de&pw!b{Woknw68M5sRx{VA!I_s>rKcXKqSvnDV7CXGJ zEh1TArC$;O_P|n@>L6E)t=dmA7TaLZ3<)rHA~*h)hDPLlzi^dJG^V?s{JqaDgpbQh z*D33((9HsO8gKhGC#cR;;CR&XgO^>JydP&A6$&xR<~D3phx+2YizwO#RxLEuf_l8Q zP1!zy+T>WWe&g4Fcv{|!&Bf(OFcT%eJLO}2es~qvaaD4m1*hD7D8{hr^@EuX*ciq$?l>XTVupvO9G#3+hKD1&k(+_u z%;#5#T7Oc*Ht+l4>LYuYvG%Cf3wuEyufN_qwi@O*pZ~?&LEFj!CnBT^mt-01c{x+# zeN(9g1NZz^k3oM)mC%o8iONPxLXS3zciA1L8FaVFYY8JQX-|hJJi#XxfuRp27RE?QUxIm8}8pz6JW9 zt@$ox<(8<|4$Y@EL!+z-KPCKAQEtA8cWbuIX&c0E=YG#8mzB&eV?~-wCJaQ6AZNhO zhhHgGZRtlPWS(jnFIySK2XYueo|x3QK0h$YVC@UQmPBCX0KgjBCay z#E#5Qk~a?mBaMlc8!*24bvUi?J8RkJLR(?zy<$}3m*TGVE;O@256D7+l9(nrO9d*! zt{To|ogs`y@d;-K{rcMd2z_(9d$c{MzjTOY1Va_XB>?Q(bT_IlcRl`*;qnOmQNXI@ zcpWESM8=VR_3EI{9=*>oT?q@gR@EClTQ6E_NU9&h`Tc`-V_H?^dU>Am>6f7sM#yxa zcJEO5iw&n^cp&KHKN7^;%y8s)+NyP;|)x~qQ4vw17#pg$SKQOkIcn*=~ znP7cZy(xE`;rJPf1;_WpA`Gzk=Puw$DCENy?`qGu%WEdo3sK7*k7Ei3);#BW$>{0E z$Xe*HvN-8T?}>%XCnEH@hvfPvk+^YP%t<6a$tUeR58Bbj_yd0&-}8bPzP)(83HBVP zR1$#-Sco^NaeRIRnZ7xK?%CkGY0EUz*Ka4Q8!terU5?Y_C&U!!bk$=p1IgOzREmKX z#uVFTg5XazV)0RUYbEs-#e zoRJ?hgQnMJP)vhuz|iC{S1y{V(48dcuP;eqAiAVmgdk?F9<>%LC!xBQ3NK$yOei)P zd-t&{FI%dC_q?bWs~hX(J4`NS`S2q8_<2aIq!qOAOs3vO_?4;#SNE#eIK;6#Rkxw= z$iC#K)y|4NjOSFVsVKCHJlRri*kZaj>1=G+-&gW^e_FfclCz$_LKxe$6EY)V0b~#R zxn%(o$=mYWMLdfrt(wbp&owGL0er{IYCXBGH&It!NBhS38il=wdDv569&`xV}xUGS5eDG`HC6L z@iWbp_wvqHRB*gq%uc8}jggWjP0zPoo~-;HDM-76BZ?325$~jWN(J&jmg%dQ^0%Oq zD)s!Uj3Hdv^wP*OHZ0-;ec2OR;u=IsLyk7P*Q&F1v2gXZS9#WG>&PIXved?A=UH_! zM$_AxGIiJ5xWOUGTb4CJ1JXNFcML)wmj{Jc8i+Z%rtF5tr}ptjBInO?G9NvL0fQ5G zV<8C8ukVIlJdn2Dr(b(RSk5IeD(NVh8!j|9)2_Xd;yNb$p9GNdNX-Q~jVTjV_;hjr z<@dX1<7S`lJtgxz#lBZZg)pup(B}FRYz1yJE`ONA#dH}~3{orhW|Fvq21RTR-UJVX z-J4#-%7PxVSa;<1Y4JbI>vZatJOe0(aB62&WK0dp)yQ&1D8)bJ=yT$i%9e-)^-?q^ z{Sxuhv|^;u=lk84%&#R&Wvzi2p{xINjgMtfdx_`oBNYQWB*$EI=@wHCOyT|)4SfE^ zq`sI)4-Z!-JEV`3J6bfr%@rJPaHX3QsCMohdnYu_1lQ}8Rie&DN20D)w9sDEuUvn$ zT`-X(L#Qqa#->THOz_w)Tall?^Okw=9TH1nt{dYxgRtmOG$w@+1RUB$|8qDm>L0pUKcMd;Qrwj!fOI@+;Vul z&ux0C9n_9->l0I4iUhF&*+_q!Aip`Lo0A4Gx=;n{uP8~nP54AEl|imZ+2$~lawBJ2 zD(SWR$CGAOx+%`bHj1BUukcPn5|-G>PXTDquc393I~n}`TMu3tqaE}Nd2!dhx|~f= z2KhHbq6E(6EGa9Lvu6|KYS(KTAh?h?249;qRPg|_OU21WxgsEBAi^sl#FtVCj#vPC zsfFO=1(fj`N3eh>9xDZ8@VF+RfJZ(-H9WcsYT~h8P!GR9G9g1eS_xU;v02CxzfHUr zf@2r<M`%>mT)x;{_ImMN(e)!f|L3AbzP} z5mrJF&4ozvFM{bMK?!#z3dF|=D8@=yeQDCbp~e37f$H^I|Ndp9Lq2mJXNY* zibw@C1{9y8-A6GFjT^=HXo4u7poyZ$2b4n51t^PR15gD;5J;8ewjQJ4u0ikt2mo-z z0RXg-Bp@!NG!G>*8$VuJNJ#Js?Mgdsub+@#!N&IR@$8h{&o3LNU-y<8;WwaVV~N;2 zyanxJaCmkKZ24n(+C2XPGd%qfX|lzpX|<_wK;LMl(zlDTFavV9E9eReMmYBW)u~g} z%qH34Ze$%kN#iCIKZF2VUAWSV=7OQna3+C=wIo#(aJEB#GySA}j=E{xZ=^ZWJo_7_ zuZTqn{DVny$+DqQY~o*#=S58vJ*h!+Ov&6<^vjy}CP~8#D!zvf{40 zm2D;9xf(Yim{7G;Zkpx>>=BBcCeJ#dLP! zDRsPRjFfnH7PekOsH6^AKHI>IbP2)Bl@y}P@>uo}mx|Gde)CBq%-EcHCP#X|%YipC z-`;EwT20p|Ss**w6#(ndZmVu=U9yoXXuo4gMjQ;4z)~-Oi)Q(RVs(p0I{<_&LLi zkO#qg@z{l~WSpyMy^=#Dbi_N%`X6{DH}dvY?F6^cl`}JDXj#pM=qw8}_bpzM!)97v z9+I7AaQx)-7wzA1jtk_!qWWfI6MOKM-o8F*(wx?Wk?@n7BuOfAYxEK)`MUe(?m5G~ zPb0yzSN@m@pli_Yh=L73{rlBt-d_WN000^W0HE;izkh>;3t!S9<0rnB!FxKOjmw;y z4GccUA~p9f6G#Sv1Tm<~+KQ&@Bqex7o5@#(;r&=}sRvTO6vdoyIaoIFo_(u%n6cpe zUR~t;dTsD2K(l&0mVuRy)$}+wb|sC<&CI8iN%+y0P{?Xn=@5!#vqZ;Txz%< z#NmRcUH+nd68_D*s#T<)*t-&25!~GZI6x-R) zeV>-&u?)m#^>R3ggWX;6*6=4n#`eGkK#dbySB2Q=mer&K5avcXIilI*M4AqV0o$zv zojFGJ94~p}1hkd*1lJ3UNlR5rXin-yKkx3!sDp2O^A`&kQST6Mx=j;=G0u@I29LKH zo)Id54+`!26hPNjgB&B&@~v6x{FyS0dL0oedo7MBOV5?`Io`mEx5L~T-3?MM3V4E; zT3OVMg-=5&vNrqhx6-$5HJvVA1RN99$q#0|6p`W6;@9(_`C%ReJMon>CKiLj`)tO_Aj9Xt*=#V=?R`kBQ8a*Xis8 zSu5B~Ioh$f{6mgD`K~by2e9t?32M6Y$^Mw*PB;bhJIGGQXQQc6N}0bE7r4X`ZHIXm zJ#h0|5xh@&@9ox-xn&{)$3zSad4gknv0|0XW@l!f@fu5rdtsZ$GFSaQpQ+9&X)|oXbsN&75$L`IR`YWjObMNu@5N($aYR;9dC-Fl5o#N{w0j}8}IY38kM4^(MZ@s0yw@}7Ug^;Ov>rH1y&Lnd8eeH=GOO5d& z&m`mx;S(}}8TiUZ%gzI-6k~>4T`7<>g&t~_hz@T-5qug>_blVYVt86D#Kx2hJ9+&1 z3y~K;n|V@5!F|H?ocYu(vThLKq^MI}U#=MZE~TXiiX{29uH^!K&H9*~wBNGoOtQ#9OVcT@kRQU%l)4gPtmkdO4vxK0 z>5ZvInF9BAJ>9vk61ayu>2}uUa%)o&OujF`iu&_4-$?Stxqw=2^SJjALdZBt+WEs* zcrIA%DULSr77sy|7Q2X-F&$CyvBO9>x~NP?bjxxb=j{nWI$w-eiCwXEMjl%n3o_PQ zHn%VGWLc7b*4F}|QQuEk?sx{wI8*Dgq{oneHO))V>hl=GFIL%Psc-@Wc#ZVWnw#Zh zw_K-?pP0;!V%~iC;(1;Y5kzBM32)kAOeJdS3uW_(H?c5W9Ue__muP<`IsbL_wR}vz z-rs|g@0;vOxRrS~0lVkQ6doZ2-J?2j$pLYVka!)ndd#jko(Gvrkmm>1bI}N&+~udC z!%ykIijC^_!V%1BnJu3{X&Vq;Txqi-jzcW~!{}xBgc*8vTUos3&c^X)hnIo!rVRtN%yE%jyAB*JFpVrMWJ9E4(C^P1-=Rqj*^O~XUr2)?z7Fc zf<}tbK&B5U47)q7dz%#6z<&!bzmHiiqWJus`s~r@QizyWgqX}wkWXiuaw=X(b2wL1 zD5w1q=I6xD2$D{%kow2s9Fmn+KzF}WVW)M0kb12yEaCb7?!cbDx!wLL83H0f;dOx} zQ7bxDMY$8RADuLq#ln@VBsOvEsm?TV+LM01Hy^L8C={#nUO7r6!@E=ZK@Hn6$)aLYHrj`nT%$HWH75JMOnJKe4fvd{b?ZK?+ z(=63p^36xh>&u>qad<`4()}ZQnG9OJ98noFD;IP{`nHu-aE#!as~aQm8|hS^4$jfF z=I4V(Tf1=GfeZ3QxA@wCH?Ik%dm~82qw#Th3tfXrsY+fl&Gl>7`PvVc72hLn5ejDR zH}!d38{m3&FwDpRhaZjY{%BzIZw=Tf+O2YSzBwDtt8%h-A$5M}-PhClK;C_F=cPDj zV%fl{ZC1Lr>rx*Up+@V2SGD-R{Hpj|mN?$^#}($oFS}+$c}C=VUG{6`(%*9O_D4er z9UBUC3+c5SkR&BGrWJmXGPuLhwxdb9X#<)_W1Ip5m2}UXs#$@tu=_+M%wz$^RSH37 zl;2wHx!sMOScJrsTQPu3H6$9w2*VHKY6QBt&#xIuy;W4mHl50og6t*b$E~sVxl8cmW zvyKMOoZWmDB&iFz2n26@g45a>U9K+R{f+^s%-ZNK!~R`}ZZFj3kpwJMs4SVCMI1#d zmZx{DZD-L$C%tPf8y(A$*RAf$+d~C_H0NVNN^|hRJE6pYQAsn`Q86YS8%_^5Vmyr# ztjc~v&59t>q`K2?r za%+?7HAWP7l%9ub-*`5JSUgIY)ewoCt!d<`4Je~@*SfM z+Iq+#bhlH0Qf+(Woe24~$ClDPJRk@5%Eao!C zx%*_)P5~01`vv2j^hfS++X=r*eo#I#hgDz+7A$>?~m(cFs<`K8_Cm+tYW( zzm@O!Ut?mx%?*J0&Wm&(^C)X5@HxU>SUd6kKV#beF=zbu`p%k%`PQF!P@NISM+>yS y6Yt!M9=XH!Paj>}-tPW=n+a(MB|?o5$|;eqPzoR=`+sb>kdshi%oFyz7yKXjiz3qi diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java index 65a6cf32ba..cf10d2e3d3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java @@ -40,9 +40,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @Slf4j public class PluginInstanceServiceImpl implements PluginInstanceService { - // TODO @haohao:这个可以后续确认下,有没更合适的标识。例如说 mac 地址之类的 - // 简化的 UUID + mac 地址 会不会好一些,一台机子有可能会部署多个插件; - // 那就 mac@uuid ? + // TODO @haohao:mac@uuid public static final String MAIN_ID = IdUtil.fastSimpleUUID(); @Resource @@ -60,32 +58,31 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @Override public void stopAndUnloadPlugin(String pluginKey) { PluginWrapper plugin = pluginManager.getPlugin(pluginKey); - // TODO @haohao:改成 if return 会更简洁一点; - if (plugin != null) { - if (plugin.getPluginState().equals(PluginState.STARTED)) { - pluginManager.stopPlugin(pluginKey); // 停止插件 - log.info("已停止插件: {}", pluginKey); - } - pluginManager.unloadPlugin(pluginKey); // 卸载插件 - log.info("已卸载插件: {}", pluginKey); - } else { + if (plugin == null) { log.warn("插件不存在或已卸载: {}", pluginKey); + return; } + if (plugin.getPluginState().equals(PluginState.STARTED)) { + pluginManager.stopPlugin(pluginKey); // 停止插件 + log.info("已停止插件: {}", pluginKey); + } + pluginManager.unloadPlugin(pluginKey); // 卸载插件 + log.info("已卸载插件: {}", pluginKey); } @Override public void deletePluginFile(PluginInfoDO pluginInfoDO) { File file = new File(pluginsDir, pluginInfoDO.getFileName()); - // TODO @haohao:改成 if return 会更简洁一点; - if (file.exists()) { - try { - TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 - if (!file.delete()) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); - } - } catch (InterruptedException e) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); + if (!file.exists()) { + return; + } + try { + TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 + if (!file.delete()) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); } + } catch (InterruptedException e) { + log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); } } @@ -120,25 +117,25 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { String pluginKey = pluginInfoDo.getPluginKey(); PluginWrapper plugin = pluginManager.getPlugin(pluginKey); - // TODO @haohao:改成 if return 会更简洁一点; - if (plugin != null) { - // 启动插件 - if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) - && plugin.getPluginState() != PluginState.STARTED) { - pluginManager.startPlugin(pluginKey); - log.info("已启动插件: {}", pluginKey); - } - // 停止插件 - else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) - && plugin.getPluginState() == PluginState.STARTED) { - pluginManager.stopPlugin(pluginKey); - log.info("已停止插件: {}", pluginKey); - } - } else { + if (plugin == null) { // 插件不存在且状态为停止,抛出异常 if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { throw exception(ErrorCodeConstants.PLUGIN_STATUS_INVALID); } + return; + } + + // 启动插件 + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) + && plugin.getPluginState() != PluginState.STARTED) { + pluginManager.startPlugin(pluginKey); + log.info("已启动插件: {}", pluginKey); + } + // 停止插件 + else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) + && plugin.getPluginState() == PluginState.STARTED) { + pluginManager.stopPlugin(pluginKey); + log.info("已停止插件: {}", pluginKey); } } @@ -152,10 +149,10 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { Map pluginInfoMap = pluginInfos.stream() .collect(Collectors.toMap(PluginInfoDO::getPluginKey, Function.identity())); - // 1.3 获取本机 IP 和 MAC 地址 + // 1.3 获取本机 IP 和 MAC 地址,mac@uuid String ip = NetUtil.getLocalhostStr(); String mac = NetUtil.getLocalMacAddress(); - String mainId = MAIN_ID + "-" + mac; + String mainId = mac + "@" + MAIN_ID; // 2. 遍历插件列表,并保存为插件实例 for (PluginWrapper plugin : plugins) { @@ -173,14 +170,21 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { pluginInfo.getId()); if (pluginInstance == null) { // 4.4 如果插件实例不存在,则创建 - pluginInstance = PluginInstanceDO.builder().pluginId(pluginInfo.getId()).mainId(MAIN_ID + "-" + mac) - .ip(ip).port(port).heartbeatAt(System.currentTimeMillis()).build(); + pluginInstance = PluginInstanceDO.builder() + .pluginId(pluginInfo.getId()) + .mainId(MAIN_ID + "-" + mac) + .ip(ip) + .port(port) + .heartbeatAt(System.currentTimeMillis()) + .build(); pluginInstanceMapper.insert(pluginInstance); } else { // 2.2 情况二:如果存在,则更新 heartbeatAt - // TODO @haohao:这里最好 new 去 update;避免并发更新(虽然目前没有) - pluginInstance.setHeartbeatAt(System.currentTimeMillis()); - pluginInstanceMapper.updateById(pluginInstance); + PluginInstanceDO updatePluginInstance = PluginInstanceDO.builder() + .id(pluginInstance.getId()) + .heartbeatAt(System.currentTimeMillis()) + .build(); + pluginInstanceMapper.updateById(updatePluginInstance); } } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java index f63267b27b..9fdb29ea85 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java @@ -5,24 +5,30 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -// TODO @haohao:类注释,写一下,比较好 +/** + * 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求, + * 包括设备状态更新、事件数据上报、属性数据上报等操作。 + */ @Slf4j +@RequiredArgsConstructor public class DeviceDataApiClient implements DeviceDataApi { + /** + * 用于发送 HTTP 请求的工具 + */ private final RestTemplate restTemplate; - private final String deviceDataUrl; - // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 - // TODO @haohao:可以用 lombok 简化 - public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) { - this.restTemplate = restTemplate; - this.deviceDataUrl = deviceDataUrl; - } + /** + * 远程 IoT 服务的基础 URL + * 例如:http://127.0.0.1:8080 + */ + private final String deviceDataUrl; // TODO @haohao:返回结果,不用 CommonResult 哈。 @Override @@ -43,17 +49,51 @@ public class DeviceDataApiClient implements DeviceDataApi { return doPost(url, reportReqDTO, "reportDevicePropertyData"); } - // TODO @haohao:未来可能有 get 类型哈 + /** - * 将与远程服务交互的通用逻辑抽取成一个私有方法 + * 发送 GET 请求 + * + * @param 请求体类型 + * @param url 请求 URL + * @param requestBody 请求体 + * @param actionName 操作名称 + * @return 响应结果 + */ + private CommonResult doGet(String url, T requestBody, String actionName) { + log.info("[{}] Sending request to URL: {}", actionName, url); + try { + CommonResult response = restTemplate.getForObject(url, CommonResult.class); + if (response != null && response.isSuccess()) { + return success(true); + } else { + log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response); + return CommonResult.error(500, "Request failed"); + } + } catch (Exception e) { + log.error("[{}] Error sending request to URL: {}", actionName, url, e); + return CommonResult.error(400, "Request error: " + e.getMessage()); + } + } + + /** + * 发送 POST 请求 + * + * @param 请求体类型 + * @param url 请求 URL + * @param requestBody 请求体 + * @param actionName 操作名称 + * @return 响应结果 */ private CommonResult doPost(String url, T requestBody, String actionName) { log.info("[{}] Sending request to URL: {}", actionName, url); try { - // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 - restTemplate.postForObject(url, requestBody, CommonResult.class); - // TODO @haohao:check 结果,是否成功 - return success(true); + CommonResult response = restTemplate.postForObject(url, requestBody, CommonResult.class); + if (response != null && response.isSuccess()) { + return success(true); + } else { + log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response); + return CommonResult.error(500, "Request failed"); + } } catch (Exception e) { log.error("[{}] Error sending request to URL: {}", actionName, url, e); return CommonResult.error(400, "Request error: " + e.getMessage()); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java deleted file mode 100644 index ed39449306..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/DeviceDataApiInitializer.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin.common.config; - -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -// TODO @haohao:这个最好是 autoconfiguration -@Configuration -public class DeviceDataApiInitializer { - - // TODO @haohao:这个要不搞个配置类哈 - @Value("${iot.device-data.url}") - private String deviceDataUrl; - - @Bean - public RestTemplate restTemplate() { - // TODO haohao:如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 - return new RestTemplateBuilder().build(); - } - - // TODO @haohao:不存在时,才构建 - @Bean - public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { - return new DeviceDataApiClient(restTemplate, deviceDataUrl); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java new file mode 100644 index 0000000000..6ba82ed5dd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.plugin.common.config; + +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; + +/** + * 设备数据 API 初始化器 + * + * @author haohao + */ +@AutoConfiguration +public class YudaoDeviceDataApiAutoConfiguration { + + + // TODO @haohao:这个要不搞个配置类哈 + @Value("${iot.device-data.url}") + private String deviceDataUrl; + + /** + * 创建 RestTemplate 实例 + * + * @return RestTemplate 实例 + */ + @Bean + public RestTemplate restTemplate() { + // 如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 + return new RestTemplateBuilder() + .setConnectTimeout(Duration.ofMillis(5000)) // 设置连接超时时间 + .setReadTimeout(Duration.ofMillis(5000)) // 设置读取超时时间 + .build(); + } + + /** + * 创建 DeviceDataApi 实例 + * + * @param restTemplate RestTemplate 实例 + * @return DeviceDataApi 实例 + */ + @Bean + public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { + return new DeviceDataApiClient(restTemplate, deviceDataUrl); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000000..65bd7ad7dd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +cn.iocoder.yudao.module.iot.plugin.common.config.YudaoDeviceDataApiAutoConfiguration \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java index 91be33097d..062b01808b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java @@ -11,7 +11,7 @@ import org.springframework.context.ConfigurableApplicationContext; * 独立运行入口 */ @Slf4j -@SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") // TODO @haohao:建议不扫描 cn.iocoder.yudao.module.iot.plugin;而是通过自动配置,初始化 common 的 +@SpringBootApplication public class HttpPluginSpringbootApplication { public static void main(String[] args) { @@ -21,6 +21,7 @@ public class HttpPluginSpringbootApplication { // 手动获取 VertxService 并启动 // TODO @haohao:可以放在 bean 的 init 里么? + // 会和插件模式冲突 VertxService vertxService = context.getBean(VertxService.class); vertxService.startServer(); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index 40694cf40c..9cc96ef102 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -21,30 +21,41 @@ public class HttpVertxPlugin extends SpringPlugin { @Override public void start() { - // TODO @haohao:这种最好启动中,启动完成,成对打印日志,方便定位问题 - log.info("[HttpVertxPlugin][start ...]"); + log.info("[HttpVertxPlugin][start][begin] 开始启动 HttpVertxPlugin 插件..."); - // 1. 获取插件上下文 - ApplicationContext pluginContext = getApplicationContext(); - if (pluginContext == null) { - log.error("[HttpVertxPlugin] pluginContext is null, start failed."); - return; + try { + // 1. 获取插件上下文 + ApplicationContext pluginContext = getApplicationContext(); + if (pluginContext == null) { + log.error("[HttpVertxPlugin][start][fail] pluginContext is null, 启动失败!"); + return; + } + + // 2. 启动 Vert.x + VertxService vertxService = pluginContext.getBean(VertxService.class); + vertxService.startServer(); + + log.info("[HttpVertxPlugin][start][end] 启动完成"); + } catch (Exception e) { + log.error("[HttpVertxPlugin][start][exception] 启动过程出现异常!", e); } - - // 2. 启动 Vertx - VertxService vertxService = pluginContext.getBean(VertxService.class); - vertxService.startServer(); } - @Override public void stop() { - log.info("[HttpVertxPlugin][stop ...]"); - ApplicationContext pluginContext = getApplicationContext(); - if (pluginContext != null) { - // 停止服务器 - VertxService vertxService = pluginContext.getBean(VertxService.class); - vertxService.stopServer(); + log.info("[HttpVertxPlugin][stop][begin] 开始停止 HttpVertxPlugin 插件..."); + + try { + ApplicationContext pluginContext = getApplicationContext(); + if (pluginContext != null) { + // 停止服务器 + VertxService vertxService = pluginContext.getBean(VertxService.class); + vertxService.stopServer(); + } + + log.info("[HttpVertxPlugin][stop][end] 停止完成"); + } catch (Exception e) { + log.error("[HttpVertxPlugin][stop][exception] 停止过程出现异常!", e); } } @@ -68,5 +79,4 @@ public class HttpVertxPlugin extends SpringPlugin { return pluginContext; } - -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java index 5c221e795a..e61a4cf8ff 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -34,6 +34,10 @@ public class HttpVertxPluginConfiguration { /** * 创建路由 + * + * @param vertx Vertx 实例 + * @param httpVertxHandler HttpVertxHandler 实例 + * @return Router 实例 */ @Bean public Router router(Vertx vertx, HttpVertxHandler httpVertxHandler) { @@ -50,6 +54,12 @@ public class HttpVertxPluginConfiguration { return router; } + /** + * 创建 HttpVertxHandler 实例 + * + * @param deviceDataApi DeviceDataApi 实例 + * @return HttpVertxHandler 实例 + */ @Bean public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { return new HttpVertxHandler(deviceDataApi); @@ -58,6 +68,10 @@ public class HttpVertxPluginConfiguration { /** * 定义一个 VertxService 来管理服务器启动逻辑 * 无论是独立运行还是插件方式,都可以共用此类 + * + * @param vertx Vertx 实例 + * @param router Router 实例 + * @return VertxService 实例 */ @Bean public VertxService vertxService(Vertx vertx, Router router) { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml index e98d46eebe..3a64c0f37e 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml @@ -5,3 +5,8 @@ spring: iot: device-data: url: http://127.0.0.1:48080 + +plugin: + http: + server: + port: 8092 From f4ad3e9d2d66ba93303ce3c7f70ff0b90527da87 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 26 Jan 2025 17:55:04 +0800 Subject: [PATCH 104/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=8F=92=E4=BB=B6=E4=BD=93?= =?UTF-8?q?=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../YudaoDeviceDataApiAutoConfiguration.java | 1 - .../http/HttpPluginSpringbootApplication.java | 3 +- .../plugin/http/config/HttpVertxPlugin.java | 29 +++++++++---------- .../src/main/resources/application.yml | 2 +- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java index 6ba82ed5dd..2c1554474e 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java @@ -18,7 +18,6 @@ import java.time.Duration; @AutoConfiguration public class YudaoDeviceDataApiAutoConfiguration { - // TODO @haohao:这个要不搞个配置类哈 @Value("${iot.device-data.url}") private String deviceDataUrl; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java index 062b01808b..bd64c8bb06 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java @@ -20,8 +20,7 @@ public class HttpPluginSpringbootApplication { ConfigurableApplicationContext context = application.run(args); // 手动获取 VertxService 并启动 - // TODO @haohao:可以放在 bean 的 init 里么? - // 会和插件模式冲突 + // TODO @haohao:可以放在 bean 的 init 里么?回复:会和插件模式冲突 @芋艿,测试下 VertxService vertxService = context.getBean(VertxService.class); vertxService.startServer(); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index 9cc96ef102..f9a589a246 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; +import cn.hutool.core.lang.Assert; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; import lombok.extern.slf4j.Slf4j; @@ -21,62 +22,60 @@ public class HttpVertxPlugin extends SpringPlugin { @Override public void start() { - log.info("[HttpVertxPlugin][start][begin] 开始启动 HttpVertxPlugin 插件..."); - + log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动开始...]"); try { // 1. 获取插件上下文 ApplicationContext pluginContext = getApplicationContext(); - if (pluginContext == null) { - log.error("[HttpVertxPlugin][start][fail] pluginContext is null, 启动失败!"); - return; - } + Assert.notNull(pluginContext, "pluginContext 不能为空"); // 2. 启动 Vert.x VertxService vertxService = pluginContext.getBean(VertxService.class); vertxService.startServer(); - log.info("[HttpVertxPlugin][start][end] 启动完成"); + log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动成功...]"); } catch (Exception e) { - log.error("[HttpVertxPlugin][start][exception] 启动过程出现异常!", e); + log.error("[HttpVertxPlugin][HttpVertxPlugin 插件开启动异常...]", e); } } @Override public void stop() { - log.info("[HttpVertxPlugin][stop][begin] 开始停止 HttpVertxPlugin 插件..."); - + log.info("[HttpVertxPlugin][HttpVertxPlugin 插件停止开始...]"); try { + // 停止服务器 ApplicationContext pluginContext = getApplicationContext(); if (pluginContext != null) { - // 停止服务器 VertxService vertxService = pluginContext.getBean(VertxService.class); vertxService.stopServer(); } - log.info("[HttpVertxPlugin][stop][end] 停止完成"); + log.info("[HttpVertxPlugin][HttpVertxPlugin 插件停止成功...]"); } catch (Exception e) { - log.error("[HttpVertxPlugin][stop][exception] 停止过程出现异常!", e); + log.error("[HttpVertxPlugin][HttpVertxPlugin 插件停止异常...]", e); } } @Override protected ApplicationContext createApplicationContext() { + // TODO @haohao:这个加 deviceDataApi 的目的是啥呀? AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext() { + @Override protected void prepareRefresh() { // 在刷新容器前注册主程序中的 Bean ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); beanFactory.registerSingleton("deviceDataApi", deviceDataApi); - super.prepareRefresh(); } + }; pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); + // TODO @芋艿:枚举 pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); pluginContext.refresh(); - return pluginContext; } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml index 3a64c0f37e..f7f89e3e67 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml @@ -9,4 +9,4 @@ iot: plugin: http: server: - port: 8092 + port: 8092 \ No newline at end of file From 8089f3a319efb919fce809b8e144c3fcc0694b4f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 14:15:07 +0800 Subject: [PATCH 105/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=201.=20DeviceDataApi=20=3D>=20Io?= =?UTF-8?q?tDeviceUpstreamApi=EF=BC=8C=E5=B9=B6=E6=96=B0=E5=BB=BA=20upstre?= =?UTF-8?q?am=20=E5=8C=85=202.=20ThingModelMessage=20=3D>=20IotDeviceMessa?= =?UTF-8?q?ge=20=E8=AE=BE=E5=A4=87=E6=B6=88=E6=81=AF=203.=20=E5=9F=BA?= =?UTF-8?q?=E4=BA=8E=20spring=20event=20=E5=BC=82=E6=AD=A5=E6=B6=88?= =?UTF-8?q?=E8=B4=B9=20IotDeviceMessage=EF=BC=8C=E5=B9=B6=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20IotDeviceLogMessageConsumer=20=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/db/TenantDatabaseInterceptor.java | 2 +- .../tenant/core/util/TenantUtils.java | 20 ++ ...DataApi.java => IotDeviceUpstreamApi.java} | 33 +-- .../dto/IotDeviceEventReportReqDTO.java | 22 +- .../dto/IotDevicePropertyReportReqDTO.java | 23 +- .../dto/IotDeviceStatusUpdateReqDTO.java | 21 +- .../dto/IotDeviceUpstreamAbstractReqDTO.java | 46 +++ .../IotDeviceMessageIdentifierEnum.java | 22 ++ .../device/IotDeviceMessageTypeEnum.java | 22 ++ ...mpl.java => IoTDeviceUpstreamApiImpl.java} | 22 +- .../admin/device/IotDeviceDataController.java | 10 +- .../dal/dataobject/device/IotDeviceDO.java | 2 + .../dal/dataobject/device/IotDeviceLogDO.java | 36 ++- .../tdengine/ThingModelMessage.java | 70 ----- .../dal/tdengine/IotDeviceLogDataMapper.java | 32 +- .../iot/emq/service/EmqxServiceImpl.java | 8 +- .../iot/framework/aspect/TaosAspect.java | 1 + .../config/SecurityConfiguration.java | 29 ++ .../framework/security/core/package-info.java | 4 + .../TDengineTableInitConfiguration.java | 4 +- .../iot/job/plugin/PluginInstancesJob.java | 1 + .../device/IotDeviceLogMessageConsumer.java | 30 ++ .../IotDevicePropertyMessageConsumer.java | 34 +++ .../deviceconsumer/DeviceConsumer.java | 41 --- .../iot/mq/consumer/rule/package-info.java | 4 + .../iot/mq/message/IotDeviceMessage.java | 66 +++++ .../module/iot/mq/message/package-info.java | 4 - .../IotDeviceProducer.java} | 13 +- .../module/iot/mq/producer/package-info.java | 4 + .../device/IotDeviceLogDataServiceImpl.java | 77 ----- .../IotDeviceLogService.java} | 20 +- .../device/data/IotDeviceLogServiceImpl.java | 60 ++++ .../IotDevicePropertyService.java} | 12 +- .../IotDevicePropertyServiceImpl.java} | 60 +--- .../upstream/IotDeviceUpstreamService.java | 37 +++ .../IotDeviceUpstreamServiceImpl.java | 103 +++++++ .../product/IotProductServiceImpl.java | 4 +- .../tdengine/IotThingModelMessageService.java | 21 -- .../IotThingModelMessageServiceImpl.java | 277 ------------------ .../mapper/device/IotDeviceLogDataMapper.xml | 33 +-- .../common/api/DeviceDataApiClient.java | 81 ++--- .../YudaoDeviceDataApiAutoConfiguration.java | 4 +- .../yudao/module/iot/plugin/EmqxPlugin.java | 4 +- .../plugin/http/config/HttpVertxPlugin.java | 4 +- .../config/HttpVertxPluginConfiguration.java | 4 +- .../plugin/http/service/HttpVertxHandler.java | 11 +- 46 files changed, 648 insertions(+), 790 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/{DeviceDataApi.java => IotDeviceUpstreamApi.java} (72%) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/{DeviceDataApiImpl.java => IoTDeviceUpstreamApiImpl.java} (69%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/{simulatesend/SimulateSendProducer.java => device/IotDeviceProducer.java} (50%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDeviceLogDataService.java => data/IotDeviceLogService.java} (55%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDevicePropertyDataService.java => data/IotDevicePropertyService.java} (82%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/{IotDevicePropertyDataServiceImpl.java => data/IotDevicePropertyServiceImpl.java} (81%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java index e220f8bcf0..8ea1a96b87 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/db/TenantDatabaseInterceptor.java @@ -13,7 +13,7 @@ import java.util.Set; /** * 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能 * - * @author + * @author 芋道源码 */ public class TenantDatabaseInterceptor implements TenantLineHandler { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java index 7ec9c69e33..b05b3c06be 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java @@ -45,6 +45,7 @@ public class TenantUtils { * * @param tenantId 租户编号 * @param callable 逻辑 + * @return 结果 */ public static V execute(Long tenantId, Callable callable) { Long oldTenantId = TenantContextHolder.getTenantId(); @@ -78,6 +79,25 @@ public class TenantUtils { } } + /** + * 忽略租户,执行对应的逻辑 + * + * @param callable 逻辑 + * @return 结果 + */ + public static V executeIgnore(Callable callable) { + Boolean oldIgnore = TenantContextHolder.isIgnore(); + try { + TenantContextHolder.setIgnore(true); + // 执行逻辑 + return callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + TenantContextHolder.setIgnore(oldIgnore); + } + } + /** * 将多租户编号,添加到 header 中 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java similarity index 72% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index c2d36e18c6..996ff1f5a3 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -5,49 +5,44 @@ import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; import cn.iocoder.yudao.module.iot.enums.ApiConstants; -import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -// TODO 芋艿:名字可能看情况改下 /** - * 设备数据 API + * 设备数据 Upstream 上行 API + * + * 目的:设备 -> 插件 -> 服务端 * * @author haohao */ -public interface DeviceDataApi { +public interface IotDeviceUpstreamApi { - // TODO @芋艿:可能会调整 - String PREFIX = ApiConstants.PREFIX + "/device-data"; + String PREFIX = ApiConstants.PREFIX + "/device/upstream"; /** * 更新设备状态 * - * @param updateReqDTO 更新请求 + * @param updateReqDTO 更新设备状态 DTO */ @PutMapping(PREFIX + "/update-status") - @PermitAll // TODO 芋艿:后续看看怎么优化下 CommonResult updateDeviceStatus(@Valid @RequestBody IotDeviceStatusUpdateReqDTO updateReqDTO); + /** + * 上报设备属性数据 + * + * @param reportReqDTO 上报设备属性数据 DTO + */ + @PostMapping(PREFIX + "/report-property") + CommonResult reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); + /** * 上报设备事件数据 * * @param reportReqDTO 设备事件 */ @PostMapping(PREFIX + "/report-event") - @PermitAll // TODO 芋艿:后续看看怎么优化下 CommonResult reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO); - /** - * 上报设备属性数据 - * - * @param reportReqDTO 设备数据 - */ - @PostMapping(PREFIX + "/report-property") - @PermitAll // TODO 芋艿:后续看看怎么优化下 - CommonResult reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); - - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java index 373905c946..6376eb8a41 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java @@ -1,10 +1,9 @@ package cn.iocoder.yudao.module.iot.api.device.dto; import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import java.util.Map; @@ -12,24 +11,9 @@ import java.util.Map; * IoT 设备【事件】数据上报 Request DTO */ @Data +@SuperBuilder @NoArgsConstructor -@AllArgsConstructor -@Builder -public class IotDeviceEventReportReqDTO { - - // TODO 芋艿:要不要 id - // TODO 芋艿:要不要 time - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; +public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { /** * 事件标识 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java index 37a4c6c984..9173a39ca9 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java @@ -1,10 +1,9 @@ package cn.iocoder.yudao.module.iot.api.device.dto; import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import java.util.Map; @@ -12,28 +11,14 @@ import java.util.Map; * IoT 设备【属性】数据上报 Request DTO */ @Data +@SuperBuilder @NoArgsConstructor -@AllArgsConstructor -@Builder -public class IotDevicePropertyReportReqDTO { +public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { - // TODO 芋艿:要不要 id - // TODO 芋艿:要不要 time - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; /** * 属性参数 */ @NotEmpty(message = "属性参数不能为空") - private Map params; + private Map properties; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java index 0b08f2bd11..111427a03c 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStatusUpdateReqDTO.java @@ -3,33 +3,18 @@ package cn.iocoder.yudao.module.iot.api.device.dto; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; /** * IoT 设备状态更新 Request DTO */ @Data +@SuperBuilder @NoArgsConstructor -@AllArgsConstructor -@Builder -public class IotDeviceStatusUpdateReqDTO { +public class IotDeviceStatusUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO { - // TODO 芋艿:要不要 id - // TODO 芋艿:要不要 time - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; /** * 设备状态 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java new file mode 100644 index 0000000000..b2a843effc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.api.device.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; + +/** + * IoT 设备上行的抽象 Request DTO + * + * @author 芋道源码 + */ +@Data +@SuperBuilder +@NoArgsConstructor +public abstract class IotDeviceUpstreamAbstractReqDTO { + + /** + * 请求编号 + */ + private String requestId; + + /** + * 插件标识 + */ + private String pluginKey; + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + /** + * 上报时间 + */ + private LocalDateTime reportTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java new file mode 100644 index 0000000000..bb639f49a2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.enums.device; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * IoT 设备消息标识符枚举 + */ +@Getter +@RequiredArgsConstructor +public enum IotDeviceMessageIdentifierEnum { + + PROPERTY_GET("get"), + PROPERTY_SET("set"), + PROPERTY_REPORT("report"); + + /** + * 标志符 + */ + private final String identifier; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java new file mode 100644 index 0000000000..b3f00e8600 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.enums.device; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * IoT 设备消息类型枚举 + */ +@Getter +@RequiredArgsConstructor +public enum IotDeviceMessageTypeEnum { + + STATE("state"), // 设备状态 + PROPERTY("property"), // 设备属性 + EVENT("event"); // 设备事件 + + /** + * 属性 + */ + private final String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java similarity index 69% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index cdcfdfdfd6..0e6df14804 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/DeviceDataApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; @@ -13,28 +13,30 @@ import javax.annotation.Resource; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** - * 设备数据 API 实现类 + * * 设备数据 Upstream 上行 API 实现类 */ @RestController @Validated -public class DeviceDataApiImpl implements DeviceDataApi { +public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { @Resource - private IotDevicePropertyDataService deviceDataService; + private IotDeviceUpstreamService deviceUpstreamService; @Override public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { - return success(true); - } - - @Override - public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + deviceUpstreamService.updateDeviceStatus(updateReqDTO); return success(true); } @Override public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - deviceDataService.saveDeviceData(reportReqDTO); + deviceUpstreamService.reportDevicePropertyData(reportReqDTO); + return success(true); + } + + @Override + public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + deviceUpstreamService.reportDeviceEventData(reportReqDTO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 638c880467..15366cf9c3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -27,13 +27,13 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; public class IotDeviceDataController { @Resource - private IotDevicePropertyDataService deviceDataService; + private IotDevicePropertyService deviceDataService; @Resource - private IotDeviceLogDataService iotDeviceLogDataService; + private IotDeviceLogService iotDeviceLogDataService; @Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性 - private IotDeviceLogDataService deviceLogDataService; + private IotDeviceLogService deviceLogDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index f396855f13..f1d5f9792f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -36,6 +36,8 @@ public class IotDeviceDO extends BaseDO { private Long id; /** * 设备唯一标识符,全局唯一,用于识别设备 + * + * 类似阿里云 QueryDeviceInfo 的 IotInstanceId */ private String deviceKey; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java index 158b0f57b9..afd0669414 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceLogDO.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; +import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,37 +23,51 @@ import lombok.NoArgsConstructor; @AllArgsConstructor public class IotDeviceLogDO { - // TODO @芋艿:消息 ID 的生成逻辑 /** - * 消息 ID + * 日志编号 + * + * 通过 {@link IdUtil#fastSimpleUUID()} 生成 */ private String id; + /** + * 请求编号 + * + * 对应 {@link IotDeviceMessage#getRequestId()} 字段 + */ + private String requestId; + /** * 产品标识 *

* 关联 {@link IotProductDO#getProductKey()} */ private String productKey; - + /** + * 设备名称 + * + * 关联 {@link IotDeviceDO#getDeviceName()} + */ + private String deviceName; /** * 设备标识 *

* 关联 {@link IotDeviceDO#getDeviceKey()}} */ - private String deviceKey; + private String deviceKey; // 非存储字段,用于 TDengine 的 TAG - // TODO @super:枚举类 /** * 日志类型 + * + * 枚举 {@link IotDeviceMessageTypeEnum} */ private String type; - - // TODO @super:枚举类 /** - * 标识符:用于标识具体的属性、事件或服务 + * 标识符 + * + * 枚举 {@link IotDeviceMessageIdentifierEnum} */ - private String subType; + private String identifier; /** * 数据内容 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java deleted file mode 100644 index d5009dc244..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessage.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.HashMap; -import java.util.Map; - -/** - * 物模型消息 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class ThingModelMessage { - - /** - * 消息ID - */ - private String id; - - /** - * 扩展功能的参数 - */ - private Object sys; - - /** - * 请求方法 例如:thing.event.property.post - */ - private String method; - - /** - * 请求参数 - */ - private Object params; - - /** - * 属性上报时间戳 - */ - private Long time; - - /** - * 设备信息 - */ - private String productKey; - - /** - * 设备名称 - */ - private String deviceName; - - /** - * 设备 key - */ - private String deviceKey; - - /** - * 转换为 Map 类型 - */ - public Map dataToMap() { - Map mapData = new HashMap<>(); - if (params instanceof Map) { - ((Map) params).forEach((key, value) -> mapData.put(key.toString(), value)); - } - return mapData; - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java index f9d18bdccd..cd9f387a71 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java @@ -22,21 +22,13 @@ public interface IotDeviceLogDataMapper { */ void createDeviceLogSTable(); - // TODO @super:单个参数,不用加 @Param - // TODO @芋艿:在瞅瞅 - //讨论:艿菇这里有些特殊情况,我也学习了一下这块知识: - // 如果使用的是Java 8及以上版本,并且编译器保留了参数名(通过编译器选项-parameters启用),则可以去掉@Param注解。MyBatis会自动使用参数的实际名称 - // 但在TDengine中 @Param去掉后TDengine会报错,以下是大模型的回答: - // 不用加 @Param在普通的 MySQL 场景下是正确的 - 对于 MyBatis,当方法只有一个参数时,确实可以不用添加 @Param 注解。 - //但是在 TDengine 的场景下,情况不同: - //TDengine 的特殊性: - //TDengine 使用特殊的 SQL 语法 - //需要处理超级表(STable)和子表的概念 - //参数绑定的方式与普通 MySQL 不同 - //为什么这里必须要 @Param: - //XML 中使用了 ${log.deviceKey} 这样的参数引用方式 - //需要在 SQL 中动态构建表名(device_log_${log.deviceKey}) - //没有 @Param("log") 的话,MyBatis 无法正确解析参数 + /** + * 查询设备日志表是否存在 + * + * @return 存在则返回表名;不存在则返回 null + */ + String showDeviceLogSTable(); + /** * 插入设备日志数据 * @@ -44,7 +36,7 @@ public interface IotDeviceLogDataMapper { * * @param log 设备日志数据 */ - void insert(@Param("log") IotDeviceLogDO log); + void insert(IotDeviceLogDO log); /** * 获得设备日志分页 @@ -62,12 +54,4 @@ public interface IotDeviceLogDataMapper { */ Long selectCount(@Param("reqVO") IotDeviceLogPageReqVO reqVO); - // TODO @芋艿:这个方法名,后续看看叫啥好 - /** - * 查询设备日志表是否存在 - * - * @return 不存在返回 null - */ - Object checkDeviceLogSTableExists(); - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index 222d1d50af..ca9a6bac7b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.emq.service; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttClient; @@ -20,7 +20,7 @@ import org.springframework.scheduling.annotation.Async; public class EmqxServiceImpl implements EmqxService { @Resource - private IotDevicePropertyDataService iotDeviceDataService; + private IotDevicePropertyService iotDeviceDataService; // TODO 多线程处理消息 @Override @@ -35,8 +35,8 @@ public class EmqxServiceImpl implements EmqxService { String deviceName = topic.split("/")[3]; String message = new String(mqttMessage.getPayload()); IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() - .productKey(productKey) - .deviceName(deviceName) +// .productKey(productKey) +// .deviceName(deviceName) // .properties(message) // TODO 芋艿:临时去掉,看看 .build(); iotDeviceDataService.saveDeviceData(createDTO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java index 7c9fe70009..61b03dc42e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/aspect/TaosAspect.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component; import java.sql.Timestamp; import java.util.Map; +// TODO @haohao:这个还需要的么? /** * TaosAspect 是一个处理 Taos 数据库返回值的切面。 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000000..9cf00cc104 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.framework.security.config; + +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; +import cn.iocoder.yudao.module.iot.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * IoT 模块的 Security 配置 + */ +@Configuration(proxyBeanMethods = false, value = "iotSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("iotAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // RPC 服务的安全配置 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java new file mode 100644 index 0000000000..c714d10274 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package cn.iocoder.yudao.module.iot.framework.security.core; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java index 9f3c79c227..3e84ac11c8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.framework.tdengine.config; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; @@ -17,7 +17,7 @@ import org.springframework.context.annotation.Configuration; @RequiredArgsConstructor public class TDengineTableInitConfiguration implements ApplicationRunner { - private final IotDeviceLogDataService deviceLogService; + private final IotDeviceLogService deviceLogService; @Override public void run(ApplicationArguments args) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java index d32148b47c..fbcfea3404 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.concurrent.TimeUnit; +// TODO 芋艿:后续再看看 /** * 插件实例 Job * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java new file mode 100644 index 0000000000..2972677918 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceLogMessageConsumer.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.device; + +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * 针对 {@link IotDeviceMessage} 的消费者,记录设备日志 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class IotDeviceLogMessageConsumer { + + @Resource + private IotDeviceLogService deviceLogService; + + @EventListener + @Async + public void onMessage(IotDeviceMessage message) { + log.info("[onMessage][消息内容({})]", message); + deviceLogService.createDeviceLog(message); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java new file mode 100644 index 0000000000..63b4c9a5e0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.device; + +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 针对 {@link IotDeviceMessage} 的消费者,记录设备属性 + * + * @author alwayssuper + */ +@Component +@Slf4j +public class IotDevicePropertyMessageConsumer { + + @Resource + private IotDevicePropertyService deviceDataService; + + @EventListener + @Async + public void onMessage(IotDeviceMessage message) { + log.info("[onMessage][消息内容({})]", message); + + // 设备日志记录 + // TODO @芋艿:重新写下 +// deviceLogDataService.createDeviceLog(message); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java deleted file mode 100644 index 7403fb6686..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/deviceconsumer/DeviceConsumer.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.iot.mq.consumer.deviceconsumer; - - -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceLogDataService; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; - -/** - * 针对 {@link ThingModelMessage} 的消费者 - * - * @author alwayssuper - */ -@Component -@Slf4j -public class DeviceConsumer { - - @Resource - private IotDeviceLogDataService deviceLogDataService; - @Resource - private IotDevicePropertyDataService deviceDataService; - - // TODO @芋艿:这块先用ThingModelMessage,后续看看用啥替代 - @EventListener - @Async - public void onMessage(ThingModelMessage message) { - log.info("[onMessage][消息内容({})]", message); - //TODO:数据插入这块整体写的比较混乱,整体借鉴了浩浩哥之前写的逻辑,目前是通过模拟设备科插入数据了,但之前的逻辑有大量弃用的部分,后续看看怎么完善 - - // 设备数据记录 - deviceDataService.saveDeviceDataTest(message); - // 设备日志记录 - deviceLogDataService.saveDeviceLog(message); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java new file mode 100644 index 0000000000..3920443172 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:未来实现一个 IotRuleMessageConsumer + */ +package cn.iocoder.yudao.module.iot.mq.consumer.rule; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java new file mode 100644 index 0000000000..db42b4fe16 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.iot.mq.message; + +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 设备消息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class IotDeviceMessage { + + /** + * 请求编号 + */ + private String requestId; + + /** + * 设备信息 + */ + private String productKey; + /** + * 设备名称 + */ + private String deviceName; + /** + * 设备标识 + */ + private String deviceKey; + + /** + * 消息类型 + * + * 枚举 {@link IotDeviceMessageTypeEnum} + */ + private String type; + /** + * 标识符 + * + * 枚举 {@link IotDeviceMessageIdentifierEnum} + */ + private String identifier; + + /** + * 请求参数 + * + * 例如说:属性上报的 properties、事件上报的 params + */ + private Object data; + + /** + * 上报时间 + */ + private LocalDateTime reportTime; + + // TODO @芋艿 code; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java deleted file mode 100644 index c3adf7c061..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 消息队列的消息 - */ -package cn.iocoder.yudao.module.iot.mq.message; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java similarity index 50% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java index 7366f4da54..c3855fbfe6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/simulatesend/SimulateSendProducer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java @@ -1,31 +1,30 @@ -package cn.iocoder.yudao.module.iot.mq.producer.simulatesend; +package cn.iocoder.yudao.module.iot.mq.producer.device; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; -// TODO @芋艿:@alwayssuper:是不是还没用起来哈?Producer 最好属于某个模块; /** - * SimulateSend 模拟设备上报的 Producer + * Iot 设备相关消息的 Producer * * @author alwayssuper * @since 2024/12/17 16:35 */ @Slf4j @Component -public class SimulateSendProducer { +public class IotDeviceProducer { @Resource private ApplicationContext applicationContext; /** - * 发送 {@link ThingModelMessage} 消息 + * 发送 {@link IotDeviceMessage} 消息 * * @param thingModelMessage 物模型消息 */ - public void sendSimulateMessage(ThingModelMessage thingModelMessage) { + public void sendDeviceMessage(IotDeviceMessage thingModelMessage) { applicationContext.publishEvent(thingModelMessage); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java new file mode 100644 index 0000000000..37d0ba016d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:临时占位 + */ +package cn.iocoder.yudao.module.iot.mq.producer; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java deleted file mode 100644 index bfe3199551..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataServiceImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.device; - -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.List; - -/** - * IoT 设备日志数据 Service 实现了 - * - * @author alwayssuper - */ -@Service -@Slf4j -@Validated -public class IotDeviceLogDataServiceImpl implements IotDeviceLogDataService{ - - @Resource - private IotDeviceLogDataMapper deviceLogDataMapper; - - @Override - public void defineDeviceLog() { - if (deviceLogDataMapper.checkDeviceLogSTableExists() != null) { - log.info("[defineDeviceLog][设备日志超级表已存在,跳过创建]"); - return; - } - - log.info("[defineDeviceLog][设备日志超级表不存在,开始创建]"); - deviceLogDataMapper.createDeviceLogSTable(); - log.info("[defineDeviceLog][设备日志超级表不存在,创建完成]"); - } - - @Override - public void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { - // 1. 转换请求对象为 DO - IotDeviceLogDO iotDeviceLogDO = BeanUtils.toBean(simulatorReqVO, IotDeviceLogDO.class); - - // 2. 处理时间字段 -// iotDeviceLogDO.setTs(currentTime); // TODO @super:TS在SQL中直接NOW 咱们的TS数据获取是走哪一种;走 now() - - // 3. 插入数据 - deviceLogDataMapper.insert(iotDeviceLogDO); - } - - @Override - public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { - // TODO @芋艿:增加一个表不存在的 try catch - List list = deviceLogDataMapper.selectPage(pageReqVO); - Long total = deviceLogDataMapper.selectCount(pageReqVO); - return new PageResult<>(list, total); - } - - @Override - public void saveDeviceLog(ThingModelMessage message) { - IotDeviceLogDO log = IotDeviceLogDO.builder() - .id(message.getId()) - .deviceKey(message.getDeviceKey()) - .productKey(message.getProductKey()) - .type(message.getMethod()) // 消息类型,使用method作为类型 TODO 芋艿:在看看 - .subType("property") // TODO 芋艿:这块先写死,后续优化 - .content(JSONUtil.toJsonStr(message)) // TODO 芋艿:后续优化 - .reportTime(message.getTime()) // 上报时间 TODO 芋艿:在想想时间 - .build(); - deviceLogDataMapper.insert(log); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java similarity index 55% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java index 637c8f51a3..ff695a7820 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java @@ -1,17 +1,16 @@ -package cn.iocoder.yudao.module.iot.service.device; +package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; /** * IoT 设备日志数据 Service 接口 * * @author alwayssuper */ -public interface IotDeviceLogDataService { +public interface IotDeviceLogService { /** * 初始化 TDengine 超级表 @@ -23,11 +22,9 @@ public interface IotDeviceLogDataService { /** * 插入设备日志 * - * 当该设备第一次插入日志时,自动创建该设备的设备日志子表 - * - * @param simulatorReqVO 设备日志模拟数据 + * @param message 设备数据 */ - void createDeviceLog(IotDeviceDataSimulatorSaveReqVO simulatorReqVO); + void createDeviceLog(IotDeviceMessage message); /** * 获得设备日志分页 @@ -37,11 +34,4 @@ public interface IotDeviceLogDataService { */ PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO); - /** - * 插入设备日志 - * - * @param message 设备数据 - */ - void saveDeviceLog(ThingModelMessage message); - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java new file mode 100644 index 0000000000..fa5398a79a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.iot.service.device.data; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +/** + * IoT 设备日志数据 Service 实现类 + * + * @author alwayssuper + */ +@Service +@Slf4j +@Validated +public class IotDeviceLogServiceImpl implements IotDeviceLogService { + + @Resource + private IotDeviceLogDataMapper deviceLogDataMapper; + + @Override + public void defineDeviceLog() { + if (StrUtil.isNotEmpty(deviceLogDataMapper.showDeviceLogSTable())) { + log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]"); + return; + } + + log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]"); + deviceLogDataMapper.createDeviceLogSTable(); + log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]"); + } + + @Override + public void createDeviceLog(IotDeviceMessage message) { + IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class) + .setId(IdUtil.fastSimpleUUID()) + .setContent(JsonUtils.toJsonString(message.getData())); + deviceLogDataMapper.insert(log); + } + + @Override + public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { + // TODO @芋艿:增加一个表不存在的 try catch + List list = deviceLogDataMapper.selectPage(pageReqVO); + Long total = deviceLogDataMapper.selectCount(pageReqVO); + return new PageResult<>(list, total); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java index 672785694e..828f2fef5e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java @@ -1,11 +1,10 @@ -package cn.iocoder.yudao.module.iot.service.device; +package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import jakarta.validation.Valid; import java.util.List; @@ -16,7 +15,7 @@ import java.util.Map; * * @author 芋道源码 */ -public interface IotDevicePropertyDataService { +public interface IotDevicePropertyService { /** * 定义设备属性数据的结构 @@ -32,13 +31,6 @@ public interface IotDevicePropertyDataService { */ void saveDeviceData(IotDevicePropertyReportReqDTO createDTO); - /** - * 保存设备数据 - * - * @param thingModelMessage 设备数据 - */ - void saveDeviceDataTest(ThingModelMessage thingModelMessage); - /** * 模拟设备 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java similarity index 81% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index abd788948f..741ba70cb3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDevicePropertyDataServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -1,9 +1,8 @@ -package cn.iocoder.yudao.module.iot.service.device; +package cn.iocoder.yudao.module.iot.service.device.data; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; @@ -16,18 +15,15 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; -import cn.iocoder.yudao.module.iot.mq.producer.simulatesend.SimulateSendProducer; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.tdengine.IotThingModelMessageService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -53,7 +49,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_C */ @Service @Slf4j -public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataService { +public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { /** * 物模型的数据类型,与 TDengine 数据类型的映射关系 @@ -76,25 +72,16 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe @Resource private IotDeviceService deviceService; @Resource - private IotThingModelMessageService thingModelMessageService; - @Resource private IotThingModelService thingModelService; @Resource private IotProductService productService; - @Resource - private SimulateSendProducer simulateSendProducer; - - @Resource - private TdEngineDMLMapper tdEngineDMLMapper; - @Resource private DeviceDataRedisDAO deviceDataRedisDAO; @Resource private IotDevicePropertyDataMapper devicePropertyDataMapper; - @Override public void defineDevicePropertyData(Long productId) { // 1.1 查询产品和物模型 @@ -144,28 +131,8 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe // 1. 根据产品 key 和设备名称,获得设备信息 IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName()); // 2. 解析消息,保存数据 - JSONObject jsonObject = new JSONObject(createDTO.getParams()); + JSONObject jsonObject = new JSONObject(createDTO.getProperties()); log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject); - ThingModelMessage thingModelMessage = ThingModelMessage.builder() - .id(jsonObject.getStr("id")) - .sys(jsonObject.get("sys")) - .method(jsonObject.getStr("method")) - .params(jsonObject.get("params")) - .time(jsonObject.getLong("time") == null ? System.currentTimeMillis() : jsonObject.getLong("time")) - .productKey(createDTO.getProductKey()) - .deviceName(createDTO.getDeviceName()) - .deviceKey(device.getDeviceKey()) - .build(); - thingModelMessageService.saveThingModelMessage(device, thingModelMessage); - } - - //TODO @芋艿:后续捋一捋这块逻辑,先借鉴一下目前的代码 - @Override - public void saveDeviceDataTest(ThingModelMessage thingModelMessage) { - // 1. 根据产品 key 和设备名称,获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(thingModelMessage.getProductKey(), thingModelMessage.getDeviceName()); - // 2. 保存数据 - thingModelMessageService.saveThingModelMessage(device, thingModelMessage); } //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 @@ -182,20 +149,17 @@ public class IotDevicePropertyDataServiceImpl implements IotDevicePropertyDataSe throw exception(DEVICE_DATA_CONTENT_JSON_PARSE_ERROR); } + // TODO @芋艿:后续优化 // 3. 构建物模型消息 - ThingModelMessage thingModelMessage = ThingModelMessage.builder() - .id(IdUtil.fastSimpleUUID()) // TODO:后续优化 - .sys(null)// TODO:这块先写死,后续优化 - .method("thing.event.property.post") // TODO:这块先写死,后续优化 - .params(contentJson) // 将 content 作为 params - .time(simulatorReqVO.getReportTime()) // 使用上报时间 - .productKey(simulatorReqVO.getProductKey()) - .deviceName(device.getDeviceName()) - .deviceKey(device.getDeviceKey()) - .build(); +// IotDeviceMessage thingModelMessage = IotDeviceMessage.builder() +// .params(contentJson) // 将 content 作为 params +// .time(simulatorReqVO.getReportTime()) // 使用上报时间 +// .productKey(simulatorReqVO.getProductKey()) +// .deviceName(device.getDeviceName()) +// .build(); // 4. 发送模拟消息 - simulateSendProducer.sendSimulateMessage(thingModelMessage); +// simulateSendProducer.sendDeviceMessage(thingModelMessage); } @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java new file mode 100644 index 0000000000..16d387d7b0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.service.device.upstream; + +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; + +/** + * 设备上行 Service 接口 + * + * 目的:设备 -> 插件 -> 服务端 + * + * @author 芋道源码 + */ +public interface IotDeviceUpstreamService { + + /** + * 更新设备状态 + * + * @param updateReqDTO 更新设备状态 DTO + */ + void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO); + + /** + * 上报设备属性数据 + * + * @param reportReqDTO 上报设备属性数据 DTO + */ + void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO); + + /** + * 上报设备事件数据 + * + * @param reportReqDTO 设备事件 + */ + void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java new file mode 100644 index 0000000000..d930d0ad08 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.iot.service.device.upstream; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceUpstreamAbstractReqDTO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; + +/** + * 设备上行 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { + + @Resource + private IotDeviceService deviceService; + + @Resource + private IotDeviceProducer deviceProducer; + + @Override + public void updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { + log.info("[updateDeviceStatus][更新设备状态: {}]", updateReqDTO); + // TODO 芋艿:插件状态 + } + + @Override + public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + // 1.1 获得设备 + log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO); + IotDeviceDO device = getDevice(reportReqDTO); + if (device == null) { + log.error("[reportDevicePropertyData][设备({}/{})不存在]", + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); + return; + } + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, reportReqDTO); + + // 2. 发送设备消息 + IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()) + .setData(reportReqDTO.getProperties()); + sendDeviceMessage(message, device); + } + + @Override + public void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + log.info("[reportDeviceEventData][上报设备事件数据: {}]", reportReqDTO); + + // TODO 芋艿:待实现 + } + + private IotDeviceDO getDevice(IotDeviceUpstreamAbstractReqDTO reqDTO) { + return TenantUtils.executeIgnore(() -> // 需要忽略租户,因为请求时,未带租户编号 + deviceService.getDeviceByProductKeyAndDeviceName(reqDTO.getProductKey(), reqDTO.getDeviceName())); + } + + private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) { + // TODO 芋艿:插件状态 + // TODO 芋艿:操作时间 + } + + private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { + // 1. 完善消息 + message.setDeviceKey(device.getDeviceKey()); + if (StrUtil.isEmpty(message.getRequestId())) { + message.setRequestId(IdUtil.fastSimpleUUID()); + } + if (message.getReportTime() == null) { + message.setReportTime(LocalDateTime.now()); + } + + // 2. 发送消息 + try { + deviceProducer.sendDeviceMessage(message); + log.info("[sendDeviceMessage][message({}) 发送消息成功]", message); + } catch (Exception e) { + log.error("[sendDeviceMessage][message({}) 发送消息失败]", message, e); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index ad3ff94e2b..3fa0bcf0b1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDevicePropertyDataService; +import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import com.baomidou.dynamic.datasource.annotation.DSTransactional; import jakarta.annotation.Resource; import org.springframework.context.annotation.Lazy; @@ -35,7 +35,7 @@ public class IotProductServiceImpl implements IotProductService { @Resource @Lazy // 延迟加载,解决循环依赖 - private IotDevicePropertyDataService devicePropertyDataService; + private IotDevicePropertyService devicePropertyDataService; @Override public Long createProduct(IotProductSaveReqVO createReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java deleted file mode 100644 index 7f52411d89..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; - -/** - * 物模型消息 Service - */ -public interface IotThingModelMessageService { - - /** - * 保存物模型消息 - * - * @param device 设备 - * @param thingModelMessage 物模型消息 - */ - void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage); - - - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java deleted file mode 100644 index c35bb3a28b..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java +++ /dev/null @@ -1,277 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.tdengine; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.FieldParser; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdFieldDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.ThingModelMessage; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDDLMapper; -import cn.iocoder.yudao.module.iot.dal.tdengine.TdEngineDMLMapper; -import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; -import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; -import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductService; -import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.*; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; - -/** - * 物模型消息 Service 实现类 - */ -@Slf4j -@Service -public class IotThingModelMessageServiceImpl implements IotThingModelMessageService { - - private static final String TAG_NOTE = "TAG"; - private static final String NOTE = "note"; - private static final String TIME = "time"; - private static final String DEVICE_KEY = "device_key"; - private static final String DEVICE_NAME = "device_name"; - private static final String PRODUCT_KEY = "product_key"; - private static final String DEVICE_TYPE = "device_type"; - - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - - @Resource - private IotThingModelService iotThingModelService; - @Resource - private IotDeviceService iotDeviceService; - @Resource - private IotProductService productService; - - @Resource - private TdEngineDDLMapper tdEngineDDLMapper; - @Resource - private TdEngineDMLMapper tdEngineDMLMapper; - - @Resource - private IotDevicePropertyDataMapper iotDevicePropertyDataMapper; - - @Resource - private DeviceDataRedisDAO deviceDataRedisDAO; - - // TODO @haohao:这个方法,可以考虑加下 1. 2. 3. 更有层次感 - @Override - @TenantIgnore - public void saveThingModelMessage(IotDeviceDO device, ThingModelMessage thingModelMessage) { - // 1. 判断设备状态,如果为未激活状态,创建数据表并更新设备状态 - if (IotDeviceStatusEnum.INACTIVE.getStatus().equals(device.getStatus())) { - // 1.1 创建设备表 -// createDeviceTable(device.getDeviceType(), device.getProductKey(), device.getDeviceName(), device.getDeviceKey()); - iotDeviceService.updateDeviceStatus(new IotDeviceStatusUpdateReqVO() - .setId(device.getId()).setStatus(IotDeviceStatusEnum.ONLINE.getStatus())); - } - - // 2. 获取设备属性并进行物模型校验,过滤非物模型属性 - Map params = thingModelMessage.dataToMap(); - List thingModelList = getValidThingModelList(thingModelMessage.getProductKey()); - if (thingModelList.isEmpty()) { - return; - } - - // 3. 过滤并收集有效的属性字段,缓存设备属性 - List schemaFieldValues = filterAndCollectValidFields(params, thingModelList, device, thingModelMessage.getTime()); - if (schemaFieldValues.size() == 0) { // 没有字段,无需保存 - return; - } - - // 4. 构建并保存设备属性数据 -// tdEngineDMLMapper.insertData(TdTableDO.builder() -// .dataBaseName(getDatabaseName()) -// .tableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())) -// .columns(schemaFieldValues) -// .build()); - // TODO:复用了旧逻辑,先过渡一下 - iotDevicePropertyDataMapper.insertDevicePropertyData(TdTableDO.builder() - .productKey(device.getProductKey()) - .deviceKey(device.getDeviceKey()) - .columns(schemaFieldValues) - .build()); - } - - private List getValidThingModelList(String productKey) { - return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); - } - -// @Override -// @TenantIgnore -// public void createSuperTable(Long productId) { -// // 1. 查询产品 -// IotProductDO product = productService.getProduct(productId); -// // 2. 创建日志超级表 -// tdThingModelMessageMapper.createSuperTable(product.getProductKey()); -// -// // 2. 获取超级表的名称和数据库名称 -// // TODO @alwayssuper:最好 databaseName、superTableName 的处理,放到 tdThinkModelMessageMapper 里。可以考虑,弄个 default 方法 -//// String databaseName = IotTdDatabaseUtils.getDatabaseName(url); -//// String superTableName = IotTdDatabaseUtils.getThingModelMessageSuperTableName(product.getProductKey()); -//// -//// // 解析物模型,获取字段列表 -//// List schemaFields = List.of( -//// TdFieldDO.builder().fieldName("time").dataType("TIMESTAMP").build(), -//// TdFieldDO.builder().fieldName("id").dataType("NCHAR").dataLength(64).build(), -//// TdFieldDO.builder().fieldName("sys").dataType("NCHAR").dataLength(2048).build(), -//// TdFieldDO.builder().fieldName("method").dataType("NCHAR").dataLength(256).build(), -//// TdFieldDO.builder().fieldName("params").dataType("NCHAR").dataLength(2048).build() -//// ); -//// // 设置超级表的标签 -//// List tagsFields = List.of( -//// TdFieldDO.builder().fieldName("device_key").dataType("NCHAR").dataLength(64).build() -//// ); -//// // 3. 创建超级表 -//// tdEngineDDLMapper.createSuperTable(new TdTableDO(databaseName, superTableName, schemaFields, tagsFields)); -// } - - private List getValidFunctionList(String productKey) { - return filterList(iotThingModelService.getProductThingModelListByProductKey(productKey), - thingModel -> IotThingModelTypeEnum.PROPERTY.getType().equals(thingModel.getType())); - } - - private List filterAndCollectValidFields(Map params, List thingModelList, IotDeviceDO device, Long time) { - // 1. 获取属性标识符集合 - Set propertyIdentifiers = convertSet(thingModelList, IotThingModelDO::getIdentifier); - - // 2. 构建属性标识符和属性的映射 - Map thingModelMap = convertMap(thingModelList, IotThingModelDO::getIdentifier); - - // 3. 过滤并收集有效的属性字段 - List schemaFieldValues = new ArrayList<>(); - //TODO:新版本是使用ts字段 -// schemaFieldValues.add(new TdFieldDO(TIME, time)); - params.forEach((key, val) -> { - if (propertyIdentifiers.contains(key)) { - schemaFieldValues.add(new TdFieldDO(key.toLowerCase(), val)); - // 缓存设备属性 - // TODO @haohao:这个缓存的写入,可以使用的时候 cache 么?被动读 - setDeviceDataCache(device, thingModelMap.get(key), val, time); - } - }); - return schemaFieldValues; - } - - /** - * 缓存设备属性 - * - * @param device 设备信息 - * @param iotThingModelDO 物模型属性 - * @param val 属性值 - * @param time 时间 - */ - private void setDeviceDataCache(IotDeviceDO device, IotThingModelDO iotThingModelDO, Object val, Long time) { - IotDeviceDataDO deviceData = IotDeviceDataDO.builder() - .productKey(device.getProductKey()) - .deviceName(device.getDeviceName()) - .identifier(iotThingModelDO.getIdentifier()) - .value(val != null ? val.toString() : null) - .updateTime(DateUtil.toLocalDateTime(new Date(time))) - .deviceId(device.getId()) - .thingModelId(iotThingModelDO.getId()) - .name(iotThingModelDO.getName()) - .dataType(iotThingModelDO.getProperty().getDataType()) - .build(); - deviceDataRedisDAO.set(deviceData); - } - - /** - * 创建设备数据表 - * - * @param deviceType 设备类型 - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @param deviceKey 设备 Key - */ - private void createDeviceTable(Integer deviceType, String productKey, String deviceName, String deviceKey) { - // 1. 获取超级表名和数据库名 - String superTableName = getProductPropertySTableName(deviceType, productKey); - String dataBaseName = getDatabaseName(); - - // 2. 获取超级表的结构信息 - List> maps = tdEngineDDLMapper.describeSuperTable(new TdTableDO(dataBaseName, superTableName)); - List tagsFieldValues = new ArrayList<>(); - if (maps != null) { - // 2.1 过滤出 TAG 类型的字段 - List> taggedNotesList = CollectionUtils.filterList(maps, map -> TAG_NOTE.equals(map.get(NOTE))); - - // 2.2 解析字段信息 - tagsFieldValues = FieldParser.parse(taggedNotesList.stream() - .map(map -> List.of(map.get("field"), map.get("type"), map.get("length"))) - .collect(Collectors.toList())); - - // 2.3 设置 TAG 字段的值 - for (TdFieldDO tagsFieldValue : tagsFieldValues) { - switch (tagsFieldValue.getFieldName()) { - case PRODUCT_KEY -> tagsFieldValue.setFieldValue(productKey); - case DEVICE_KEY -> tagsFieldValue.setFieldValue(deviceKey); - case DEVICE_NAME -> tagsFieldValue.setFieldValue(deviceName); - case DEVICE_TYPE -> tagsFieldValue.setFieldValue(deviceType); - } - } - } - - // 3. 创建设备数据表 - String tableName = getDeviceTableName(productKey, deviceName); - tdEngineDDLMapper.createTable(TdTableDO.builder().build() - .setDataBaseName(dataBaseName) - .setSuperTableName(superTableName) - .setTableName(tableName) - .setTags(tagsFieldValues)); - } - - - - /** - * 获取数据库名称 - * - * @return 数据库名称 - */ - private String getDatabaseName() { - return StrUtil.subAfter(url, "/", true); - } - - /** - * 获取产品属性表名 - * - * @param deviceType 设备类型 - * @param productKey 产品 Key - * @return 产品属性表名 - */ - private static String getProductPropertySTableName(Integer deviceType, String productKey) { - // TODO @haohao:枚举下,会好点哈。 - return switch (deviceType) { - case 1 -> String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); - case 2 -> String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); - default -> String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); - }; - } - - /** - * 获取设备表名 - * - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @return 设备表名 - */ - private static String getDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey.toLowerCase(), deviceName.toLowerCase()); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml index 039180857b..bbc1fb7185 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml @@ -9,8 +9,9 @@ ts TIMESTAMP, id NCHAR(50), product_key NCHAR(50), + device_name NCHAR(50), type NCHAR(50), - sub_type NCHAR(50), + identifier NCHAR(255), content NCHAR(1024), report_time TIMESTAMP ) TAGS ( @@ -18,18 +19,23 @@ ) + + - INSERT INTO device_log_${log.deviceKey} (ts, id, product_key, type, subType, content, report_time) + INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time) USING device_log - TAGS ('${log.deviceKey}') + TAGS ('${deviceKey}') VALUES ( NOW, - #{log.id}, - #{log.productKey}, - #{log.type}, - #{log.subType}, - #{log.content}, - #{log.reportTime} + #{id}, + #{productKey}, + #{deviceName}, + #{type}, + #{identifier}, + #{content}, + #{reportTime} ) @@ -51,6 +57,7 @@ LIMIT #{reqVO.pageSize} OFFSET #{reqVO.pageNo} + - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java index 9fdb29ea85..a10e9ec3d5 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java @@ -1,99 +1,62 @@ package cn.iocoder.yudao.module.iot.plugin.common.api; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -/** - * 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求, - * 包括设备状态更新、事件数据上报、属性数据上报等操作。 - */ +// TODO @haohao:类注释,写一下,比较好 +// TODO @haohao:类名要改下 @Slf4j -@RequiredArgsConstructor -public class DeviceDataApiClient implements DeviceDataApi { +public class DeviceDataApiClient implements IotDeviceUpstreamApi { + + public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; - /** - * 用于发送 HTTP 请求的工具 - */ private final RestTemplate restTemplate; - - /** - * 远程 IoT 服务的基础 URL - * 例如:http://127.0.0.1:8080 - */ private final String deviceDataUrl; + // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 + // TODO @haohao:可以用 lombok 简化 + public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) { + this.restTemplate = restTemplate; + this.deviceDataUrl = deviceDataUrl; + } + // TODO @haohao:返回结果,不用 CommonResult 哈。 @Override public CommonResult updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) { - String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status"; + String url = deviceDataUrl + URL_PREFIX + "/update-status"; return doPost(url, updateReqDTO, "updateDeviceStatus"); } @Override public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { - String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event"; + String url = deviceDataUrl + URL_PREFIX + "/report-event"; return doPost(url, reportReqDTO, "reportDeviceEventData"); } @Override public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property"; + String url = deviceDataUrl + URL_PREFIX + "/report-property"; return doPost(url, reportReqDTO, "reportDevicePropertyData"); } - + // TODO @haohao:未来可能有 get 类型哈 /** - * 发送 GET 请求 - * - * @param 请求体类型 - * @param url 请求 URL - * @param requestBody 请求体 - * @param actionName 操作名称 - * @return 响应结果 - */ - private CommonResult doGet(String url, T requestBody, String actionName) { - log.info("[{}] Sending request to URL: {}", actionName, url); - try { - CommonResult response = restTemplate.getForObject(url, CommonResult.class); - if (response != null && response.isSuccess()) { - return success(true); - } else { - log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response); - return CommonResult.error(500, "Request failed"); - } - } catch (Exception e) { - log.error("[{}] Error sending request to URL: {}", actionName, url, e); - return CommonResult.error(400, "Request error: " + e.getMessage()); - } - } - - /** - * 发送 POST 请求 - * - * @param 请求体类型 - * @param url 请求 URL - * @param requestBody 请求体 - * @param actionName 操作名称 - * @return 响应结果 + * 将与远程服务交互的通用逻辑抽取成一个私有方法 */ private CommonResult doPost(String url, T requestBody, String actionName) { log.info("[{}] Sending request to URL: {}", actionName, url); try { - CommonResult response = restTemplate.postForObject(url, requestBody, CommonResult.class); - if (response != null && response.isSuccess()) { - return success(true); - } else { - log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response); - return CommonResult.error(500, "Request failed"); - } + // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 + restTemplate.postForObject(url, requestBody, CommonResult.class); + // TODO @haohao:check 结果,是否成功 + return success(true); } catch (Exception e) { log.error("[{}] Error sending request to URL: {}", actionName, url, e); return CommonResult.error(400, "Request error: " + e.getMessage()); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java index 2c1554474e..ef613d5be6 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.common.config; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -43,7 +43,7 @@ public class YudaoDeviceDataApiAutoConfiguration { * @return DeviceDataApi 实例 */ @Bean - public DeviceDataApi deviceDataApi(RestTemplate restTemplate) { + public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) { return new DeviceDataApiClient(restTemplate, deviceDataUrl); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java index 27b90426b2..b5fed5518b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin; import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; import org.pf4j.PluginWrapper; @@ -27,7 +27,7 @@ public class EmqxPlugin extends Plugin { executorService = Executors.newSingleThreadExecutor(); } - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); if (deviceDataApi == null) { log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); return; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index f9a589a246..2c263673ae 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.hutool.core.lang.Assert; import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPlugin; @@ -64,7 +64,7 @@ public class HttpVertxPlugin extends SpringPlugin { protected void prepareRefresh() { // 在刷新容器前注册主程序中的 Bean ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); beanFactory.registerSingleton("deviceDataApi", deviceDataApi); super.prepareRefresh(); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java index e61a4cf8ff..af54d1f532 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; @@ -61,7 +61,7 @@ public class HttpVertxPluginConfiguration { * @return HttpVertxHandler 实例 */ @Bean - public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { + public HttpVertxHandler httpVertxHandler(IotDeviceUpstreamApi deviceDataApi) { return new HttpVertxHandler(deviceDataApi); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java index df55c68fd6..ec4431d2d1 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java @@ -2,19 +2,21 @@ package cn.iocoder.yudao.module.iot.plugin.http.service; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import io.vertx.core.Handler; import io.vertx.ext.web.RequestBody; import io.vertx.ext.web.RoutingContext; import lombok.extern.slf4j.Slf4j; +import java.util.Map; + @Slf4j public class HttpVertxHandler implements Handler { - private final DeviceDataApi deviceDataApi; + private final IotDeviceUpstreamApi deviceDataApi; - public HttpVertxHandler(DeviceDataApi deviceDataApi) { + public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) { this.deviceDataApi = deviceDataApi; } @@ -23,6 +25,7 @@ public class HttpVertxHandler implements Handler { String productKey = ctx.pathParam("productKey"); String deviceName = ctx.pathParam("deviceName"); + // TODO @haohao:requestBody.asJsonObject() 貌似天然就是 json 对象哈? RequestBody requestBody = ctx.body(); JSONObject jsonData; try { @@ -43,7 +46,7 @@ public class HttpVertxHandler implements Handler { IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder() .productKey(productKey) .deviceName(deviceName) - .params(jsonData) + .properties((Map) requestBody.asJsonObject().getMap().get("properties")) .build(); deviceDataApi.reportDevicePropertyData(reportReqDTO); From b319485ca60539fd2f343920c3f97d09cd13d8ee Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 14:23:34 +0800 Subject: [PATCH 106/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E6=B8=85=E7=90=86=E9=80=9A?= =?UTF-8?q?=E7=94=A8=20TDengine=20=E5=B0=81=E8=A3=85=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20SQL=20=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/tdengine/FieldParser.java | 85 ---------- .../iot/dal/dataobject/tdengine/SelectDO.java | 48 ------ .../dal/dataobject/tdengine/TagsSelectDO.java | 37 ----- .../dal/dataobject/tdengine/TdFieldDO.java | 49 ------ .../dal/dataobject/tdengine/TdResponse.java | 42 ----- .../dal/dataobject/tdengine/TdRestApi.java | 59 ------- .../dal/dataobject/tdengine/TdTableDO.java | 71 --------- .../tdengine/ThingModelMessageDO.java | 61 -------- .../tdengine/IotDevicePropertyDataMapper.java | 22 --- .../iot/dal/tdengine/TdEngineDDLMapper.java | 147 ------------------ .../iot/dal/tdengine/TdEngineDMLMapper.java | 103 ------------ ...tion.java => TDengineTableInitRunner.java} | 10 +- .../mapper/tdengine/TdEngineDDLMapper.xml | 101 ------------ .../mapper/tdengine/TdEngineDMLMapper.xml | 86 ---------- .../tdengine/TdThinkModelMessageMapper.xml | 35 ----- 15 files changed, 5 insertions(+), 951 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/{TDengineTableInitConfiguration.java => TDengineTableInitRunner.java} (74%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java deleted file mode 100644 index 2e4e0f507e..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/FieldParser.java +++ /dev/null @@ -1,85 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelRespVO; - -import java.util.HashMap; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * FieldParser 类用于解析和转换物模型字段到 TDengine 字段 - */ -public class FieldParser { - - /** - * 物模型到td数据类型映射 - */ - @Deprecated - private static final HashMap TYPE_MAPPING = new HashMap<>() { - { - put("INT", "INT"); - put("FLOAT", "FLOAT"); - put("DOUBLE", "DOUBLE"); - put("BOOL", "BOOL"); - put("ENUM", "NCHAR"); - put("TEXT", "NCHAR"); - put("DATE", "NCHAR"); - } - }; - - /** - * 将物模型字段转换为td字段 - * - * @param property 物模型属性 - * @return TdField对象 - */ - public static TdFieldDO parse(ThingModelProperty property) { - // 将物模型字段类型映射为 td 字段类型 - String fieldName = property.getIdentifier().toLowerCase(); - String fType = TYPE_MAPPING.get(property.getDataType().toUpperCase()); - // 如果字段类型为NCHAR,默认长度为64 - int dataLength = 0; - if ("NCHAR".equals(fType)) { - dataLength = 64; - } - return new TdFieldDO(fieldName, fType, dataLength); - } - - /** - * 从物模型中获取字段列表 - * - * @param thingModel 物模型响应对象 - * @return 字段列表 - */ - public static List parse(ThingModelRespVO thingModel) { - return convertList(thingModel.getModel().getProperties(), FieldParser::parse); - } - - /** - * 将从库中查出来的字段信息转换为 TDengine 字段对象 - * - * @param rows 从库中查出的字段信息列表 - * @return 转换后的 TDengine 字段对象列表 - */ - public static List parse(List> rows) { - return convertList(rows, row -> { - String type = row.get(1).toString().toUpperCase(); - // TODO @puhui999:"NCHAR" 最好枚举下 - int dataLength = "NCHAR".equals(type) ? Integer.parseInt(row.get(2).toString()) : -1; - return new TdFieldDO(row.get(0).toString(), type, dataLength); - }); - } - - /** - * 获取字段字义 - */ - @Deprecated - public static String getFieldDefine(TdFieldDO field) { - return "`" + field.getFieldName() + "`" + " " - + (field.getDataLength() > 0 ? String.format("%s(%d)", field.getDataType(), field.getDataLength()) - : field.getDataType()); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java deleted file mode 100644 index fb4cd459a6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/SelectDO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.Data; - -// TODO @haohao:类似这个,其实可以参考 mybatis plus,querywrapper,搞个 TdEngineQueryWrapper。这样看起来会更好懂。 -/** - * 查询DO - */ -@Data -@Deprecated -public class SelectDO { - - // TODO @haoha:database 是个单词 - /** - * 数据库名称 - */ - private String dataBaseName; - - /** - * 超级表名称 - */ - private String tableName; - - /** - * 查询字段 - */ - private String fieldName; - - /** - * 开始时间 - */ - private Long startTime; - - /** - * 结束时间 - */ - private Long endTime; - - /** - * 查询类型 - */ - private String type; - - /** - * 设备ID - */ - private String deviceId; -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java deleted file mode 100644 index 3538308241..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TagsSelectDO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.Data; - -/** - * tags查询DO - */ -@Data -@Deprecated -public class TagsSelectDO { - - /** - * 数据库名称 - */ - private String dataBaseName; - - /** - * 超级表名称 - */ - private String stableName; - - /** - * tags名称 - */ - private String tagsName; - - /** - * tags值 - */ - private Long startTime; - - /** - * tags值 - */ - private Long endTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java deleted file mode 100644 index 10c9b9fe7d..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdFieldDO.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -// TODO 芋艿:看看是不是后续简化掉。 -/** - * TD 引擎的字段 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Deprecated -public class TdFieldDO { - - /** - * 字段名称 - */ - private String fieldName; - - /** - * 字段类型 - */ - private String dataType; - - /** - * 字段长度 - */ - private Integer dataLength = 0; - - /** - * 字段值 - */ - private Object fieldValue; - - public TdFieldDO(String fieldName, String dataType, Integer dataLength) { - this.fieldName = fieldName; - this.dataType = dataType; - this.dataLength = dataLength; - } - - public TdFieldDO(String fieldName, Object fieldValue) { - this.fieldName = fieldName; - this.fieldValue = fieldValue; - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java deleted file mode 100644 index 8b64f6854d..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * TdResponse 类用于处理 TDengine 的响应 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Deprecated -public class TdResponse { - - public static final int CODE_SUCCESS = 0; - public static final int CODE_TB_NOT_EXIST = 866; - - /** - * 状态 - */ - private String status; - - /** - * 错误码 - */ - private int code; - - /** - * 错误信息 - */ - private String desc; - - /** - * 列信息 - * [["time","TIMESTAMP",8,""],["powerstate","TINYINT",1,""],["brightness","INT",4,""],["deviceid","NCHAR",32,"TAG"]] - */ - private List data; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java deleted file mode 100644 index 1bb793a45a..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdRestApi.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpResponse; -import cn.hutool.json.JSONUtil; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -// TODO @haohao:有部分非实体的部分,是不是可以搞到 iot 的 framework 包下,搞个 tdengine 包,作为框架级的封装,放在 dataobject,感觉不是很合理哈。【可以微信讨论下】 -/** - * TdRestApi 类用于处理 TDengine 的 REST API 请求 - */ -@Slf4j -@Service -@Deprecated // TODO 芋艿:貌似没用到 -public class TdRestApi { - - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - - @Value("${spring.datasource.dynamic.datasource.tdengine.username}") - private String username; - - @Value("${spring.datasource.dynamic.datasource.tdengine.password}") - private String password; - - /** - * 获取 REST API URL - */ - private String getRestApiUrl() { - String restUrl = url.replace("jdbc:TAOS-RS://", "") - .replaceAll("\\?.*", ""); - int idx = restUrl.lastIndexOf("/"); - return String.format("%s/rest/sql/%s", restUrl.substring(0, idx), restUrl.substring(idx + 1)); - } - - - /** - * 新建td api请求对象 - */ - public HttpRequest newApiRequest(String sql) { - return HttpRequest - .post(getRestApiUrl()) - .body(sql) - .basicAuth(username, password); - } - - /** - * 执行sql - */ - public TdResponse execSql(String sql) { - log.info("exec td sql:{}", sql); - HttpRequest request = newApiRequest(sql); - HttpResponse response = request.execute(); - return JSONUtil.toBean(response.body(), TdResponse.class); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java deleted file mode 100644 index 104be26837..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/TdTableDO.java +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Deprecated -/** - * TD 引擎的数据库 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class TdTableDO { - - /** - * 数据库名称 - */ - private String dataBaseName; - - // TODO @haohao:superTableName 和 tableName 是不是合并。因为每个 mapper 操作的时候,有且只会使用到其中一个。 - /** - * 超级表名称 - */ - private String superTableName; - - /** - * 表名称 - */ - private String tableName; - - private String productKey; - - private String deviceKey; - - /** - * COLUMN 字段 - */ - private TdFieldDO column; - - /** - * TAG 字段 - */ - private TdFieldDO tag; - - /** - * COLUMN 字段 - 列表 - */ - private List columns; - - /** - * TAG 字段 - 列表 - */ - private List tags; - - public TdTableDO(String dataBaseName, String superTableName, List columns, List tags) { - this.dataBaseName = dataBaseName; - this.superTableName = superTableName; - this.columns = columns; - this.tags = tags; - } - - public TdTableDO(String dataBaseName, String superTableName) { - this.dataBaseName = dataBaseName; - this.superTableName = superTableName; - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java deleted file mode 100644 index ae70da37bb..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/tdengine/ThingModelMessageDO.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.tdengine; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -// TODO @芋艿:纠结下字段 -@Deprecated // TODO @super:看看啥时候删除下哈。 -/** - * TD 物模型消息日志的数据库 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class ThingModelMessageDO { - - - - /** - * 消息 ID - */ - private String id; - - /** - * 系统扩展参数 - * - * 例如:设备状态、系统时间、固件版本等系统级信息 - */ - private Object system; - - /** - * 请求方法 - * - * 例如:thing.event.property.post - */ - private String method; - - /** - * 请求参数 - */ - private Object params; - - /** - * 属性上报时间戳 - */ - private Long time; - - /** - * 设备信息 - */ - private String productKey; - - - /** - * 设备 key - */ - private String deviceKey; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index 1599ce957c..42b5e01eb8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; @@ -78,27 +77,6 @@ public interface IotDevicePropertyDataMapper { void alterProductPropertySTableDropField(@Param("productKey") String productKey, @Param("field") TDengineTableField field); - //TODO:先参考一下老逻辑,后续改进 - /** - * 插入数据 - 指定列插入数据 - * - * @param table 数据 - * productKey 产品 key - * deviceKey 设备 key - * columns 列 - */ - void insertDevicePropertyData(TdTableDO table); - - //TODO:先参考一下老逻辑,后续改进 - /** - * 查看超级表 - 获取超级表的结构信息 - * SQL:DESCRIBE [db_name.]stb_name; - * - * @param superTable 超级表信息 - * productKey 产品 key - */ - List> describeSuperTable(TdTableDO superTable); - /** * 获取历史数据列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java deleted file mode 100644 index eeb061cc55..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDDLMapper.java +++ /dev/null @@ -1,147 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import com.baomidou.dynamic.datasource.annotation.DS; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; - -import java.util.List; -import java.util.Map; - -/** - * 专门处理 DDL(数据定义语言)操作,包含所有的数据库和表的定义操作,例如创建数据库、创建超级表、创建子表等 - */ -@Mapper -@DS("tdengine") -public interface TdEngineDDLMapper { - - /** - * 创建数据库 - * SQL:CREATE DATABASE [IF NOT EXISTS] db_name [database_options]; - * - * @param dataBaseName 数据库名称 - */ - @TenantIgnore - void createDatabase(@Param("dataBaseName") String dataBaseName); - - /** - * 创建超级表 - * SQL:CREATE STABLE [IF NOT EXISTS] stb_name (create_definition [, create_definition] ...) TAGS (create_definition [, create_definition] ...) [table_options]; - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * columns 列信息 - * tags 标签信息 - */ - @TenantIgnore - void createSuperTable(TdTableDO superTable); - - /** - * 查看超级表 - 显示当前数据库下的所有超级表信息 - * SQL:SHOW STABLES [LIKE tb_name_wildcard]; - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - */ - @TenantIgnore - List> showSuperTables(TdTableDO superTable); - - /** - * 查看超级表 - 获取超级表的结构信息 - * SQL:DESCRIBE [db_name.]stb_name; - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - */ - @TenantIgnore - List> describeSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 增加列 - * SQL:ALTER STABLE stb_name ADD COLUMN col_name column_type; - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * column 列信息 - */ - @TenantIgnore - void addColumnForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 删除列 - * SQL:ALTER STABLE stb_name DROP COLUMN col_name; - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * column 列信息 - */ - @TenantIgnore - void dropColumnForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 修改列宽 - * SQL:ALTER STABLE stb_name MODIFY COLUMN col_name data_type(length); - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * column 列信息 - */ - @TenantIgnore - void modifyColumnWidthForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 为超级表添加标签 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tag 标签信息 - */ - @TenantIgnore - void addTagForSuperTable(TdTableDO superTable); - - /** - * 修改超级表 - 为超级表删除标签 - * - * @param superTable 超级表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tag 标签信息 - */ - @TenantIgnore - void dropTagForSuperTable(TdTableDO superTable); - - /** - * 创建子表 - 创建子表 - * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name TAGS (tag_value1, ...); - * - * @param table 表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tableName 子表名称 - * tags TAG 字段 - */ - @TenantIgnore - void createTable(TdTableDO table); - - /** - * 创建子表 - 创建子表并指定标签的值 - * SQL:CREATE TABLE [IF NOT EXISTS] tb_name USING stb_name (tag_name1, ...) TAGS (tag_value1, ...); - * - * @param table 表信息 - * dataBaseName 数据库名称 - * superTableName 超级表名称 - * tableName 子表名称 - * tags TAG 字段 - */ - @TenantIgnore - void createTableWithTags(TdTableDO table); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java deleted file mode 100644 index 12b2c232c3..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/TdEngineDMLMapper.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.tdengine; - -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TagsSelectDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.TdTableDO; -import com.baomidou.dynamic.datasource.annotation.DS; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; -import java.util.Map; - -/** - * 专门处理 TD Engine 的 DML(数据操作语言)操作,处理所有的数据查询和写入操作,如插入数据、查询数据等 - */ -@Mapper -@DS("tdengine") -public interface TdEngineDMLMapper { - - /** - * 插入数据 - 指定列插入数据 - * - * @param table 数据 - * dataBaseName 数据库名 - * tableName 表名 - * columns 列 - */ - @TenantIgnore - void insertData(TdTableDO table); - - /** - * 根据时间戳查询数据 - * - * @param selectDO 查询条件 - * @return 查询结果列表 - */ - @TenantIgnore - List> selectByTimestamp(SelectDO selectDO); - - /** - * 根据时间戳获取数据条数 - * - * @param selectDO 查询条件 - * @return 数据条数 - */ - @TenantIgnore - Map selectCountByTimestamp(SelectDO selectDO); - - /** - * 获取最新数据 - * - * @param selectDO 查询条件 - * @return 最新数据 - */ - @TenantIgnore - Map selectOneLastData(SelectDO selectDO); - - /** - * 获取历史数据列表 - * - * @param selectVisualDO 查询条件 - * @return 历史数据列表 - */ - @TenantIgnore - List> selectHistoryDataList(SelectVisualDO selectVisualDO); - - /** - * 获取实时数据列表 - * - * @param selectVisualDO 查询条件 - * @return 实时数据列表 - */ - @TenantIgnore - List> selectRealtimeDataList(SelectVisualDO selectVisualDO); - - /** - * 获取聚合数据列表 - * - * @param selectVisualDO 查询条件 - * @return 聚合数据列表 - */ - @TenantIgnore - List> selectAggregateDataList(SelectVisualDO selectVisualDO); - - /** - * 根据标签获取最新数据列表 - * - * @param tagsSelectDO 查询条件 - * @return 最新数据列表 - */ - @TenantIgnore - List> selectLastDataListByTags(TagsSelectDO tagsSelectDO); - - /** - * 获取历史数据条数 - * - * @param selectVisualDO 查询条件 - * @return 数据条数 - */ - @TenantIgnore - Long selectHistoryCount(SelectVisualDO selectVisualDO); -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java similarity index 74% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java index 3e84ac11c8..3517e1e58c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/config/TDengineTableInitRunner.java @@ -5,17 +5,17 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; -import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; /** * TDengine 表初始化的 Configuration * * @author alwayssuper */ -@Configuration -@Slf4j +@Component @RequiredArgsConstructor -public class TDengineTableInitConfiguration implements ApplicationRunner { +@Slf4j +public class TDengineTableInitRunner implements ApplicationRunner { private final IotDeviceLogService deviceLogService; @@ -26,7 +26,7 @@ public class TDengineTableInitConfiguration implements ApplicationRunner { deviceLogService.defineDeviceLog(); } catch (Exception ex) { // 初始化失败时打印错误日志并退出系统 - log.error("[TDengine] 初始化设备日志表结构失败,系统无法正常运行,即将退出", ex); + log.error("[run][TDengine初始化设备日志表结构失败,系统无法正常运行,即将退出]", ex); System.exit(1); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml deleted file mode 100644 index 091b77f642..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDDLMapper.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - CREATE DATABASE IF NOT EXISTS ${dataBaseName} - - - - - CREATE STABLE IF NOT EXISTS ${dataBaseName}.${superTableName} - - ${item.fieldName} ${item.dataType} - - (${item.dataLength}) - - - TAGS - - ${item.fieldName} ${item.dataType} - - (${item.dataLength}) - - - - - - - - - - - - - ALTER STABLE ${dataBaseName}.${superTableName} ADD COLUMN ${column.fieldName} ${column.dataType} - - (${column.dataLength}) - - - - - - ALTER STABLE ${dataBaseName}.${superTableName} DROP COLUMN ${column.fieldName} - - - - - ALTER STABLE ${dataBaseName}.${superTableName} MODIFY COLUMN ${column.fieldName} ${column.dataType} - - (${column.dataLength}) - - - - - - ALTER STABLE ${dataBaseName}.${superTableName} ADD TAG ${tag.fieldName} ${tag.dataType} - - (${tag.dataLength}) - - - - - - ALTER STABLE ${dataBaseName}.${superTableName} DROP TAG ${tag.fieldName} - - - - - CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} - USING ${dataBaseName}.${superTableName} - TAGS - - #{item.fieldValue} - - - - - - CREATE TABLE IF NOT EXISTS ${dataBaseName}.${tableName} - USING ${dataBaseName}.${superTableName} - - #{item.fieldName} - - TAGS - - #{item.fieldValue} - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml deleted file mode 100644 index b4084374cf..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdEngineDMLMapper.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - INSERT INTO ${dataBaseName}.${tableName} - - ${item.fieldName} - - VALUES - - #{item.fieldValue} - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml deleted file mode 100644 index 474bb7c54c..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/tdengine/TdThinkModelMessageMapper.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - CREATE STABLE thing_model_message_${productKey}( - ts TIMESTAMP, - id NCHAR(64), - sys NCHAR(2048), - method NCHAR(255), - params NCHAR(2048), - device_name NCHAR(64) - )TAGS ( - device_key NCHAR(50) - ) - - - - - CREATE STABLE ${deviceKey} - USING thing_model_message_${productKey}( - ts, - id , - sys , - method , - params , - device_name - )TAGS( - #{device_key} - ) - - \ No newline at end of file From 8e80a53a8bb37fe40f9a1d1ee5f243ce0f7b1ed5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 16:50:10 +0800 Subject: [PATCH 107/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E9=83=A8=E5=88=86=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20IotDevicePropertyMessageConsumer=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=BC=93=E5=AD=98=E7=9A=84=E8=AE=B0=E5=BD=95=EF=BC=88?= =?UTF-8?q?=E5=B7=AE=E8=AE=BE=E5=A4=87=E5=B1=9E=E6=80=A7=E7=9A=84=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceDataController.java | 8 +- .../dataobject/device/IotDeviceDataDO.java | 82 --------- .../device/IotDevicePropertyDO.java | 35 ++++ .../iot/dal/redis/RedisKeyConstants.java | 19 +- .../redis/device/DevicePropertyRedisDAO.java | 54 ++++++ .../device/DeviceReportTimeRedisDAO.java | 27 +++ .../redis/deviceData/DeviceDataRedisDAO.java | 43 ----- .../tdengine/IotDevicePropertyDataMapper.java | 1 + .../iot/emq/service/EmqxServiceImpl.java | 2 +- .../IotDevicePropertyMessageConsumer.java | 12 +- .../iot/service/device/IotDeviceService.java | 1 + .../service/device/IotDeviceServiceImpl.java | 2 - .../device/data/IotDevicePropertyService.java | 10 +- .../data/IotDevicePropertyServiceImpl.java | 163 ++++++++++-------- .../thingmodel/IotThingModelService.java | 1 + 15 files changed, 245 insertions(+), 215 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 15366cf9c3..8012ec2ec4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; @@ -28,18 +28,14 @@ public class IotDeviceDataController { @Resource private IotDevicePropertyService deviceDataService; - @Resource - private IotDeviceLogService iotDeviceLogDataService; - - @Resource // TODO @super:service 之间,不用空行;原因是,这样更简洁;空行,主要是为了“间隔”,提升可读性 private IotDeviceLogService deviceLogDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") @Operation(summary = "获取设备属性最新数据") public CommonResult> getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { - List list = deviceDataService.getLatestDeviceProperties(deviceDataReqVO); + List list = deviceDataService.getLatestDeviceProperties(deviceDataReqVO); return success(BeanUtils.toBean(list, IotDeviceDataRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java deleted file mode 100644 index 63b732d20f..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDataDO.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.dataobject.device; - -import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * IoT 设备数据 DO - * - * @author haohao - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class IotDeviceDataDO { - - /** - * 设备编号 - *

- * 关联 {@link IotDeviceDO#getId()} - */ - private Long deviceId; - - /** - * 物模型编号 - *

- * 关联 {@link IotThingModelDO#getId()} - */ - private Long thingModelId; - - /** - * 产品标识 - *

- * 关联 {@link IotProductDO#getProductKey()} - */ - private String productKey; - - /** - * 设备名称 - *

- * 冗余 {@link IotDeviceDO#getDeviceName()} - */ - private String deviceName; - - /** - * 属性标识符 - *

- * 关联 {@link IotThingModelDO#getIdentifier()} - */ - private String identifier; - - /** - * 属性名称 - *

- * 关联 {@link IotThingModelDO#getName()} - */ - private String name; - - /** - * 数据类型 - *

- * 关联 {@link IotThingModelDO#getProperty()#getDataType()} - */ - private String dataType; - - /** - * 更新时间 - */ - private LocalDateTime updateTime; - - /** - * 最新值 - */ - private String value; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java new file mode 100644 index 0000000000..afb3288941 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDevicePropertyDO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.device; + +import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * IoT 设备属性项 Redis DO + * + * @see cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants#DEVICE_PROPERTY + * @see DevicePropertyRedisDAO + * + * @author haohao + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotDevicePropertyDO { + + /** + * 属性值(最新) + */ + private Object value; + + /** + * 更新时间 + */ + private LocalDateTime updateTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java index 25ea7a2926..04858248fa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.redis; - +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; /** * Iot Redis Key 枚举类 @@ -10,11 +10,20 @@ package cn.iocoder.yudao.module.iot.dal.redis; public interface RedisKeyConstants { /** - * 设备属性数据缓存 + * 设备属性数据缓存,采用 HASH 结构 *

- * KEY 格式:device_property_data:{deviceId} - * VALUE 数据类型:String 设备属性数据 + * KEY 格式:device_property:{deviceKey} + * HASH KEY:identifier 属性标识 + * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO} */ - String DEVICE_PROPERTY_DATA = "device_property_data:%s_%s_%s"; + String DEVICE_PROPERTY = "device_property:%s"; + + /** + * 设备的最后上报时间,采用 ZSET 结构 + * + * KEY 格式:{deviceKey} + * SCORE:上报时间 + */ + String DEVICE_REPORT_TIME = "device_report_time"; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java new file mode 100644 index 0000000000..fa25b84720 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DevicePropertyRedisDAO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.iot.dal.redis.device; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.util.Collections; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY; + +/** + * {@link IotDevicePropertyDO} 的 Redis DAO + */ +@Repository +public class DevicePropertyRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + public Map get(String deviceKey) { + String redisKey = formatKey(deviceKey); + Map entries = stringRedisTemplate.opsForHash().entries(redisKey); + if (CollUtil.isEmpty(entries)) { + return Collections.emptyMap(); + } + return convertMap(entries.values(), key -> (String) key, + value -> JsonUtils.parseObject((String) value, IotDevicePropertyDO.class)); + } + + public void set(String deviceKey, Map properties) { + if (CollUtil.isEmpty(properties)) { + return; + } + String redisKey = formatKey(deviceKey); + stringRedisTemplate.opsForHash().putAll(redisKey, convertMap(properties.entrySet(), + Map.Entry::getKey, + entry -> JsonUtils.toJsonString(entry.getValue()))); + } + + public void delete(String deviceKey) { + String redisKey = formatKey(deviceKey); + stringRedisTemplate.delete(redisKey); + } + + private static String formatKey(String deviceKey) { + return String.format(DEVICE_PROPERTY, deviceKey); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java new file mode 100644 index 0000000000..35c99a06e0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/device/DeviceReportTimeRedisDAO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.dal.redis.device; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; + +/** + * 设备的最后上报时间的 Redis DAO + * + * @author 芋道源码 + */ +@Repository +public class DeviceReportTimeRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + public void update(String deviceKey, LocalDateTime reportTime) { + stringRedisTemplate.opsForZSet().add(RedisKeyConstants.DEVICE_REPORT_TIME, deviceKey, + LocalDateTimeUtil.toEpochMilli(reportTime)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java deleted file mode 100644 index b1b69f8dce..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/deviceData/DeviceDataRedisDAO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.redis.deviceData; - -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; -import jakarta.annotation.Resource; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants.DEVICE_PROPERTY_DATA; - -/** - * {@link IotDeviceDataDO} 的 Redis DAO - */ -@Repository -public class DeviceDataRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public IotDeviceDataDO get(String productKey, String deviceName, String identifier) { - String redisKey = formatKey(productKey, deviceName, identifier); - return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), IotDeviceDataDO.class); - } - - public void set(IotDeviceDataDO deviceData) { - String redisKey = formatKey(deviceData.getProductKey(), deviceData.getDeviceName(), deviceData.getIdentifier()); - stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(deviceData)); - } - - public void delete(String productKey, String deviceName, String identifier) { - String redisKey = formatKey(productKey, deviceName, identifier); - stringRedisTemplate.delete(redisKey); - } - - private static String formatKey(String productKey, String deviceName, String identifier) { - return String.format(DEVICE_PROPERTY_DATA, productKey, deviceName, identifier); - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index 42b5e01eb8..e8df639c29 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -77,6 +77,7 @@ public interface IotDevicePropertyDataMapper { void alterProductPropertySTableDropField(@Param("productKey") String productKey, @Param("field") TDengineTableField field); + // TODO @芋艿:待实现 /** * 获取历史数据列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java index ca9a6bac7b..6d45b9be9a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/emq/service/EmqxServiceImpl.java @@ -39,7 +39,7 @@ public class EmqxServiceImpl implements EmqxService { // .deviceName(deviceName) // .properties(message) // TODO 芋艿:临时去掉,看看 .build(); - iotDeviceDataService.saveDeviceData(createDTO); +// iotDeviceDataService.saveDeviceProperty(createDTO); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java index 63b4c9a5e0..3f7fe10890 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.iot.mq.consumer.device; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import lombok.extern.slf4j.Slf4j; @@ -24,11 +27,14 @@ public class IotDevicePropertyMessageConsumer { @EventListener @Async public void onMessage(IotDeviceMessage message) { + if (ObjectUtil.notEqual(message.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType()) + || ObjectUtil.notEqual(message.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier())) { + return; + } log.info("[onMessage][消息内容({})]", message); - // 设备日志记录 - // TODO @芋艿:重新写下 -// deviceLogDataService.createDeviceLog(message); + // 保存设备属性 + deviceDataService.saveDeviceProperty(message); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index adf3304d02..f01460a1b5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -113,6 +113,7 @@ public interface IotDeviceService { */ Long getDeviceCountByGroupId(Long groupId); + // TODO @芋艿:增加缓存 /** * 根据产品 key 和设备名称,获得设备信息 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 6a28b27f34..683570916f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -247,7 +246,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @Override - @TenantIgnore public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) { return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java index 828f2fef5e..b5fce2bc17 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java @@ -1,10 +1,10 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import jakarta.validation.Valid; import java.util.List; @@ -27,9 +27,9 @@ public interface IotDevicePropertyService { /** * 保存设备数据 * - * @param createDTO 设备数据 + * @param message 设备消息 */ - void saveDeviceData(IotDevicePropertyReportReqDTO createDTO); + void saveDeviceProperty(IotDeviceMessage message); /** * 模拟设备 @@ -44,7 +44,7 @@ public interface IotDevicePropertyService { * @param deviceId 设备编号 * @return 设备属性最新数据 */ - List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId); + List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceId); /** * 获得设备属性历史数据 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index 741ba70cb3..61355e02df 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -1,27 +1,26 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.DateUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDataDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; -import cn.iocoder.yudao.module.iot.dal.redis.deviceData.DeviceDataRedisDAO; +import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; @@ -31,15 +30,13 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import java.time.ZoneId; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_CONTENT_JSON_PARSE_ERROR; /** @@ -77,7 +74,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { private IotProductService productService; @Resource - private DeviceDataRedisDAO deviceDataRedisDAO; + private DevicePropertyRedisDAO deviceDataRedisDAO; @Resource private IotDevicePropertyDataMapper devicePropertyDataMapper; @@ -126,13 +123,41 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } @Override - public void saveDeviceData(IotDevicePropertyReportReqDTO createDTO) { - // TODO 芋艿:这块需要实现 - // 1. 根据产品 key 和设备名称,获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(createDTO.getProductKey(), createDTO.getDeviceName()); - // 2. 解析消息,保存数据 - JSONObject jsonObject = new JSONObject(createDTO.getProperties()); - log.info("[saveDeviceData][productKey({}) deviceName({}) data({})]", createDTO.getProductKey(), createDTO.getDeviceName(), jsonObject); + public void saveDeviceProperty(IotDeviceMessage message) { + if (!(message.getData() instanceof Map)) { + log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); + return; + } + // 1. 获得设备信息 + IotDeviceDO device = TenantUtils.executeIgnore(() -> + deviceService.getDeviceByProductKeyAndDeviceName(message.getProductKey(), message.getDeviceName())); + if (device == null) { + log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message); + return; + } + + // 2. 根据物模型,拼接合法的属性 + List thingModels = TenantUtils.executeIgnore(() -> + thingModelService.getThingModelListByProductId(device.getProductId())); + Map properties = new HashMap<>(); + ((Map) message.getData()).forEach((key, value) -> { + if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) { + log.error("[saveDeviceProperty][消息({}) 的属性({}) 不存在]", message, key); + return; + } + properties.put((String) key, value); + }); + if (CollUtil.isEmpty(properties)) { + log.error("[saveDeviceProperty][消息({}) 没有合法的属性]", message); + return; + } + + // 3.1 保存属性【数据】 + // TODO 芋艿,未实现 + + // 3.2 保存属性【日志】 + deviceDataRedisDAO.set(message.getDeviceKey(), convertMap(properties.entrySet(), Map.Entry::getKey, + entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build())); } //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 @@ -163,62 +188,64 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } @Override - public List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { - List list = new ArrayList<>(); - // 1. 获取设备信息 - IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); - // 2. 获取设备属性最新数据 - List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); - thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType() - .equals(thingModel.getType())); - - // 3. 过滤标识符和属性名称 - if (deviceDataReqVO.getIdentifier() != null) { - thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier() - .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())); - } - if (deviceDataReqVO.getName() != null) { - thingModelList = filterList(thingModelList, thingModel -> thingModel.getName() - .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())); - } - // 4. 获取设备属性最新数据 - thingModelList.forEach(thingModel -> { - IotDeviceDataDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier()); - if (deviceData == null) { - deviceData = new IotDeviceDataDO(); - deviceData.setProductKey(device.getProductKey()); - deviceData.setDeviceName(device.getDeviceName()); - deviceData.setIdentifier(thingModel.getIdentifier()); - deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); - deviceData.setThingModelId(thingModel.getId()); - deviceData.setName(thingModel.getName()); - deviceData.setDataType(thingModel.getProperty().getDataType()); - } - list.add(deviceData); - }); - return list; + public List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { +// List list = new ArrayList<>(); +// // 1. 获取设备信息 +// IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); +// // 2. 获取设备属性最新数据 +// List thingModelList = thingModelService.getProductThingModelListByProductKey(device.getProductKey()); +// thingModelList = filterList(thingModelList, thingModel -> IotThingModelTypeEnum.PROPERTY.getType() +// .equals(thingModel.getType())); +// +// // 3. 过滤标识符和属性名称 +// if (deviceDataReqVO.getIdentifier() != null) { +// thingModelList = filterList(thingModelList, thingModel -> thingModel.getIdentifier() +// .toLowerCase().contains(deviceDataReqVO.getIdentifier().toLowerCase())); +// } +// if (deviceDataReqVO.getName() != null) { +// thingModelList = filterList(thingModelList, thingModel -> thingModel.getName() +// .toLowerCase().contains(deviceDataReqVO.getName().toLowerCase())); +// } +// // 4. 获取设备属性最新数据 +// thingModelList.forEach(thingModel -> { +// IotDevicePropertyDO deviceData = deviceDataRedisDAO.get(device.getProductKey(), device.getDeviceName(), thingModel.getIdentifier()); +// if (deviceData == null) { +// deviceData = new IotDevicePropertyDO(); +// deviceData.setProductKey(device.getProductKey()); +// deviceData.setDeviceName(device.getDeviceName()); +// deviceData.setIdentifier(thingModel.getIdentifier()); +// deviceData.setDeviceId(deviceDataReqVO.getDeviceId()); +// deviceData.setThingModelId(thingModel.getId()); +// deviceData.setName(thingModel.getName()); +// deviceData.setDataType(thingModel.getProperty().getDataType()); +// } +// list.add(deviceData); +// }); +// return list; + return null; // TODO 芋艿:晚点实现 } @Override public PageResult> getHistoryDeviceProperties(IotDeviceDataPageReqVO deviceDataReqVO) { - PageResult> pageResult = new PageResult<>(); - // 1. 获取设备信息 - IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); - // 2. 获取设备属性历史数据 - SelectVisualDO selectVisualDO = new SelectVisualDO(); - selectVisualDO.setDataBaseName(getDatabaseName()); - selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); - selectVisualDO.setDeviceKey(device.getDeviceKey()); - selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier()); - selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); - selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); - Map params = new HashMap<>(); - params.put("rows", deviceDataReqVO.getPageSize()); - params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); - selectVisualDO.setParams(params); - pageResult.setList(devicePropertyDataMapper.selectHistoryDataList(selectVisualDO)); - pageResult.setTotal(devicePropertyDataMapper.selectHistoryCount(selectVisualDO)); - return pageResult; +// PageResult> pageResult = new PageResult<>(); +// // 1. 获取设备信息 +// IotDeviceDO device = deviceService.getDevice(deviceDataReqVO.getDeviceId()); +// // 2. 获取设备属性历史数据 +// SelectVisualDO selectVisualDO = new SelectVisualDO(); +// selectVisualDO.setDataBaseName(getDatabaseName()); +// selectVisualDO.setTableName(getDeviceTableName(device.getProductKey(), device.getDeviceName())); +// selectVisualDO.setDeviceKey(device.getDeviceKey()); +// selectVisualDO.setFieldName(deviceDataReqVO.getIdentifier()); +// selectVisualDO.setStartTime(DateUtil.date(deviceDataReqVO.getTimes()[0].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); +// selectVisualDO.setEndTime(DateUtil.date(deviceDataReqVO.getTimes()[1].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()).getTime()); +// Map params = new HashMap<>(); +// params.put("rows", deviceDataReqVO.getPageSize()); +// params.put("page", (deviceDataReqVO.getPageNo() - 1) * deviceDataReqVO.getPageSize()); +// selectVisualDO.setParams(params); +// pageResult.setList(devicePropertyDataMapper.selectHistoryDataList(selectVisualDO)); +// pageResult.setTotal(devicePropertyDataMapper.selectHistoryCount(selectVisualDO)); +// return pageResult; + return null; // TODO 芋艿:晚点实现 } private String getDatabaseName() { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index d68b3429e4..71d47a53f5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -46,6 +46,7 @@ public interface IotThingModelService { */ IotThingModelDO getThingModel(Long id); + // TODO @芋艿:增加缓存 /** * 获得产品物模型列表 * From 043d82e5b6e88c3533974a62911dd7dab796ff07 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 17:10:59 +0800 Subject: [PATCH 108/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=AE=8C=E6=95=B4=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20saveDeviceProperty=20=E7=9A=84=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=97=A5=E5=BF=97=E7=9A=84=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tdengine/IotDevicePropertyDataMapper.java | 4 +++ .../data/IotDevicePropertyServiceImpl.java | 34 ++++++++----------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index e8df639c29..11a3207d10 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; @@ -77,6 +78,9 @@ public interface IotDevicePropertyDataMapper { void alterProductPropertySTableDropField(@Param("productKey") String productKey, @Param("field") TDengineTableField field); + void insert(@Param("device") IotDeviceDO device, + @Param("properties") Map properties); + // TODO @芋艿:待实现 /** * 获取历史数据列表 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index 61355e02df..acf42d7aff 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; @@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; -import cn.iocoder.yudao.module.iot.enums.IotConstants; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; @@ -27,7 +26,6 @@ import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -63,9 +61,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { .put(IotDataSpecsDataTypeEnum.ARRAY.getDataType(), TDengineTableField.TYPE_NCHAR) // TODO 芋艿:怎么映射!!!! .build(); - @Value("${spring.datasource.dynamic.datasource.tdengine.url}") - private String url; - @Resource private IotDeviceService deviceService; @Resource @@ -123,22 +118,21 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } @Override + @TenantIgnore // TODO @芋艿:租户的缓存问题,需要考虑下。因为会存在一会又 tenantId,一会没有! public void saveDeviceProperty(IotDeviceMessage message) { if (!(message.getData() instanceof Map)) { log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); return; } // 1. 获得设备信息 - IotDeviceDO device = TenantUtils.executeIgnore(() -> - deviceService.getDeviceByProductKeyAndDeviceName(message.getProductKey(), message.getDeviceName())); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(message.getProductKey(), message.getDeviceName()); if (device == null) { log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message); return; } // 2. 根据物模型,拼接合法的属性 - List thingModels = TenantUtils.executeIgnore(() -> - thingModelService.getThingModelListByProductId(device.getProductId())); + List thingModels = thingModelService.getThingModelListByProductId(device.getProductId()); Map properties = new HashMap<>(); ((Map) message.getData()).forEach((key, value) -> { if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) { @@ -152,10 +146,10 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { return; } - // 3.1 保存属性【数据】 - // TODO 芋艿,未实现 + // 3.1 保存设备属性【数据】 + devicePropertyDataMapper.insert(device, properties); - // 3.2 保存属性【日志】 + // 3.2 保存设备属性【日志】 deviceDataRedisDAO.set(message.getDeviceKey(), convertMap(properties.entrySet(), Map.Entry::getKey, entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build())); } @@ -248,12 +242,12 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { return null; // TODO 芋艿:晚点实现 } - private String getDatabaseName() { - return StrUtil.subAfter(url, "/", true); - } - - private static String getDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); - } +// private String getDatabaseName() { +// return StrUtil.subAfter(url, "/", true); +// } +// +// private static String getDeviceTableName(String productKey, String deviceName) { +// return String.format(IotConstants.DEVICE_TABLE_NAME_FORMAT, productKey, deviceName); +// } } \ No newline at end of file From 7745035fa4d43bced70a8ea96b35711ac624ea01 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 21:32:33 +0800 Subject: [PATCH 109/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E6=97=A5=E5=BF=97=E8=A1=A8=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20report=5Ftime=20=E4=B8=8A=E6=8A=A5=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tdengine/IotDevicePropertyDataMapper.java | 8 +++-- .../tdengine/core/TDengineTableField.java | 5 --- .../data/IotDevicePropertyServiceImpl.java | 5 +-- .../device/IotDevicePropertyDataMapper.xml | 32 +++++++++++-------- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java index 11a3207d10..ac212cac47 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.tdengine.SelectVisualDO; @@ -30,8 +31,8 @@ public interface IotDevicePropertyDataMapper { default void alterProductPropertySTable(String productKey, List oldFields, List newFields) { - oldFields.removeIf(field -> TDengineTableField.FIELD_TS.equals(field.getField()) - || TDengineTableField.FIELD_DEVICE_KEY.equals(field.getField())); + oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), + TDengineTableField.FIELD_TS, "device_key", "report_time")); List addFields = newFields.stream().filter( // 新增的字段 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) .collect(Collectors.toList()); @@ -79,7 +80,8 @@ public interface IotDevicePropertyDataMapper { @Param("field") TDengineTableField field); void insert(@Param("device") IotDeviceDO device, - @Param("properties") Map properties); + @Param("properties") Map properties, + @Param("reportTime") Long reportTime); // TODO @芋艿:待实现 /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java index 427c681892..e3bbdd204f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java @@ -17,11 +17,6 @@ public class TDengineTableField { */ public static final String FIELD_TS = "ts"; - /** - * 字段名 - 我们系统定义的 device_key 字段,非 TDengine 默认字段 - */ - public static final String FIELD_DEVICE_KEY = "device_key"; - public static final String TYPE_TINYINT = "TINYINT"; public static final String TYPE_INT = "INT"; public static final String TYPE_FLOAT = "FLOAT"; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index acf42d7aff..fba2fd6f30 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; @@ -97,7 +98,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); return; } - newFields.add(0, new TDengineTableField(TDengineTableField.FIELD_TS, TDengineTableField.TYPE_TIMESTAMP)); devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); return; } @@ -147,7 +147,8 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } // 3.1 保存设备属性【数据】 - devicePropertyDataMapper.insert(device, properties); + devicePropertyDataMapper.insert(device, properties, + LocalDateTimeUtil.toEpochMilli(message.getReportTime())); // TODO @芋艿:后续要看看,查询的时候,能不能用 LocalDateTime // 3.2 保存设备属性【日志】 deviceDataRedisDAO.set(message.getDeviceKey(), convertMap(properties.entrySet(), Map.Entry::getKey, diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml index 3ac238d751..21d1ed16e9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml @@ -9,13 +9,16 @@ - CREATE STABLE product_property_${productKey} - + CREATE STABLE product_property_${productKey} ( + ts TIMESTAMP, + report_time TIMESTAMP, + ${field.field} ${field.type} (${field.length}) + ) TAGS ( device_key NCHAR(50) ) @@ -42,19 +45,20 @@ DROP COLUMN ${field.field} - - INSERT INTO device_property_${deviceKey} - USING product_property_${productKey} - TAGS ('${deviceKey}') - (ts - - ,${item.fieldName} + + + INSERT INTO device_property_${device.deviceKey} + USING product_property_${device.productKey} + TAGS ('${device.deviceKey}') + (ts, report_time, + + ${key} - ) - VALUES - (NOW - - ,#{item.fieldValue} + ) + VALUES + (NOW, #{reportTime}, + + #{value} ) From a364153d4a9a34375151d8527a5ad54ed60f6c22 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Jan 2025 22:23:31 +0800 Subject: [PATCH 110/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9Adevice=20=E5=92=8C=20thingmodel?= =?UTF-8?q?=20=E8=AF=BB=E5=8F=96=E5=A2=9E=E5=8A=A0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mysql/thingmodel/IotThingModelMapper.java | 8 ++-- .../iot/dal/redis/RedisKeyConstants.java | 18 +++++++- .../iot/service/device/IotDeviceService.java | 7 +-- .../service/device/IotDeviceServiceImpl.java | 43 ++++++++++++++++++- .../data/IotDevicePropertyServiceImpl.java | 6 +-- .../IotDeviceUpstreamServiceImpl.java | 9 +--- .../thingmodel/IotThingModelService.java | 19 ++++---- .../thingmodel/IotThingModelServiceImpl.java | 37 ++++++++++++++-- 8 files changed, 115 insertions(+), 32 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java index 1258893c57..4c563c65eb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -49,6 +49,10 @@ public interface IotThingModelMapper extends BaseMapperX { return selectList(IotThingModelDO::getProductId, productId); } + default List selectListByProductKey(String productKey) { + return selectList(IotThingModelDO::getProductKey, productKey); + } + default List selectListByProductIdAndType(Long productId, Integer type) { return selectList(IotThingModelDO::getProductId, productId, IotThingModelDO::getType, type); @@ -68,8 +72,4 @@ public interface IotThingModelMapper extends BaseMapperX { IotThingModelDO::getName, name); } - default List selectListByProductKey(String productKey) { - return selectList(IotThingModelDO::getProductKey, productKey); - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java index 04858248fa..d7096b55dd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java @@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; public interface RedisKeyConstants { /** - * 设备属性数据缓存,采用 HASH 结构 + * 设备属性的数据缓存,采用 HASH 结构 *

* KEY 格式:device_property:{deviceKey} * HASH KEY:identifier 属性标识 @@ -26,4 +26,20 @@ public interface RedisKeyConstants { */ String DEVICE_REPORT_TIME = "device_report_time"; + /** + * 设备信息的数据缓存,使用 Spring Cache 操作 + * + * KEY 格式:device_${productKey}_${deviceKey} + * VALUE 数据类型:String(JSON) + */ + String DEVICE = "device"; + + /** + * 物模型的数据缓存,使用 Spring Cache 操作 + * + * KEY 格式:thing_model_${productKey} + * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表 + */ + String THING_MODEL_LIST = "thing_model_list"; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index f01460a1b5..3efc4054fe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -113,15 +113,16 @@ public interface IotDeviceService { */ Long getDeviceCountByGroupId(Long groupId); - // TODO @芋艿:增加缓存 /** - * 根据产品 key 和设备名称,获得设备信息 + * 【缓存】根据产品 key 和设备名称,获得设备信息 + * + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! * * @param productKey 产品 key * @param deviceName 设备名称 * @return 设备信息 */ - IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName); + IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); /** * 导入设备 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 683570916f..786e8e0ef0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -4,22 +4,27 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -78,6 +83,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); // 2.1 转换 VO 为 DO + // TODO @芋艿:state 相关的参数。另外,到底叫 state,还是 status 好! IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> { o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); // 生成并设置必要的字段 @@ -109,6 +115,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 更新到数据库 IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); deviceMapper.updateById(updateObj); + + // 3. 清空对应缓存 + deleteDeviceCache(device); } @Override @@ -125,6 +134,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 3. 更新设备分组 deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO() .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds()))); + + // 4. 清空对应缓存 + deleteDeviceCache(devices); } @Override @@ -138,6 +150,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 删除设备 deviceMapper.deleteById(id); + + // 3. 清空对应缓存 + deleteDeviceCache(device); } @Override @@ -160,6 +175,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 删除设备 deviceMapper.deleteByIds(ids); + + // 3. 清空对应缓存 + deleteDeviceCache(devices); } /** @@ -213,6 +231,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Override public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { + // TODO @芋艿:state 相关的参数。另外,到底叫 state,还是 status 好! + // TODO @芋艿:各种时间,需要 check 下,优化处理下! // 1. 校验存在 IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); @@ -233,6 +253,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { } // 2.3 更新到数据库 deviceMapper.updateById(updateDevice); + + // 3. 清空对应缓存 + deleteDeviceCache(device); } @Override @@ -246,7 +269,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @Override - public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) { + @TenantIgnore + @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") + public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); } @@ -367,4 +392,20 @@ public class IotDeviceServiceImpl implements IotDeviceService { return respVO; } + private void deleteDeviceCache(IotDeviceDO device) { + // 保证在 @CacheEvict 之前,忽略租户 + TenantUtils.executeIgnore(() -> getSelf().deleteDeviceCache0(device)); + } + + private void deleteDeviceCache(List devices) { + devices.forEach(this::deleteDeviceCache); + } + + @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") + public void deleteDeviceCache0(IotDeviceDO device) {} + + private IotDeviceServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index fba2fd6f30..3f4c90b24f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -118,21 +118,21 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } @Override - @TenantIgnore // TODO @芋艿:租户的缓存问题,需要考虑下。因为会存在一会又 tenantId,一会没有! + @TenantIgnore public void saveDeviceProperty(IotDeviceMessage message) { if (!(message.getData() instanceof Map)) { log.error("[saveDeviceProperty][消息内容({}) 的 data 类型不正确]", message); return; } // 1. 获得设备信息 - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceName(message.getProductKey(), message.getDeviceName()); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(message.getProductKey(), message.getDeviceName()); if (device == null) { log.error("[saveDeviceProperty][消息({}) 对应的设备不存在]", message); return; } // 2. 根据物模型,拼接合法的属性 - List thingModels = thingModelService.getThingModelListByProductId(device.getProductId()); + List thingModels = thingModelService.getThingModelListByProductKeyFromCache(device.getProductKey()); Map properties = new HashMap<>(); ((Map) message.getData()).forEach((key, value) -> { if (CollUtil.findOne(thingModels, thingModel -> thingModel.getIdentifier().equals(key)) == null) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java index d930d0ad08..4a456406ca 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.device.upstream; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO; @@ -47,7 +46,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { // 1.1 获得设备 log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO); - IotDeviceDO device = getDevice(reportReqDTO); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); if (device == null) { log.error("[reportDevicePropertyData][设备({}/{})不存在]", reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); @@ -71,11 +71,6 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { // TODO 芋艿:待实现 } - private IotDeviceDO getDevice(IotDeviceUpstreamAbstractReqDTO reqDTO) { - return TenantUtils.executeIgnore(() -> // 需要忽略租户,因为请求时,未带租户编号 - deviceService.getDeviceByProductKeyAndDeviceName(reqDTO.getProductKey(), reqDTO.getDeviceName())); - } - private void updateDeviceLastTime(IotDeviceDO deviceDO, IotDeviceUpstreamAbstractReqDTO reqDTO) { // TODO 芋艿:插件状态 // TODO 芋艿:操作时间 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index 71d47a53f5..cc81e5f02f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -46,7 +46,6 @@ public interface IotThingModelService { */ IotThingModelDO getThingModel(Long id); - // TODO @芋艿:增加缓存 /** * 获得产品物模型列表 * @@ -55,6 +54,16 @@ public interface IotThingModelService { */ List getThingModelListByProductId(Long productId); + /** + * 【缓存】获得产品物模型列表 + * + * 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! + * + * @param productKey 产品标识 + * @return 产品物模型列表 + */ + List getThingModelListByProductKeyFromCache(String productKey); + /** * 获得产品物模型分页 * @@ -63,14 +72,6 @@ public interface IotThingModelService { */ PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO); - /** - * 获得产品物模型列表 - * - * @param productKey 产品 Key - * @return 产品物模型列表 - */ - List getProductThingModelListByProductKey(String productKey); - /** * 获得产品物模型列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index f0ce947d52..5c9f5fd051 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -2,8 +2,11 @@ package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; @@ -14,11 +17,14 @@ import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.mysql.thingmodel.IotThingModelMapper; +import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.product.IotProductStatusEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.*; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; @@ -69,6 +75,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? + + // 7. 删除缓存 + deleteThingModelListCache(createReqVO.getProductKey()); return thingModel.getId(); } @@ -92,6 +101,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } + + // 6. 删除缓存 + deleteThingModelListCache(updateReqVO.getProductKey()); } @Override @@ -113,6 +125,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { if (Objects.equals(thingModel.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(thingModel.getProductId(), thingModel.getProductKey()); } + + // 4. 删除缓存 + deleteThingModelListCache(thingModel.getProductKey()); } @Override @@ -126,13 +141,15 @@ public class IotThingModelServiceImpl implements IotThingModelService { } @Override - public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { - return thingModelMapper.selectPage(pageReqVO); + @TenantIgnore + @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") + public List getThingModelListByProductKeyFromCache(String productKey) { + return thingModelMapper.selectListByProductKey(productKey); } @Override - public List getProductThingModelListByProductKey(String productKey) { - return thingModelMapper.selectListByProductKey(productKey); + public PageResult getProductThingModelPage(IotThingModelPageReqVO pageReqVO) { + return thingModelMapper.selectPage(pageReqVO); } @Override @@ -333,4 +350,16 @@ public class IotThingModelServiceImpl implements IotThingModelService { .setDirection(direction.getDirection())); } + private void deleteThingModelListCache(String productKey) { + // 保证在 @CacheEvict 之前,忽略租户 + TenantUtils.executeIgnore(() -> getSelf().deleteThingModelListCache0(productKey)); + } + + @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") + public void deleteThingModelListCache0(String productKey) {} + + private IotThingModelServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + } From eb2d4fdbc06b6416cfd73bd138b2fecba927ae74 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 28 Jan 2025 03:58:24 +0800 Subject: [PATCH 111/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=90=8C=E6=AD=A5=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/plugin/IotPluginDeployTypeEnum.java | 8 ++++---- .../module/iot/enums/plugin/IotPluginStatusEnum.java | 8 ++++---- .../yudao/module/iot/enums/plugin/IotPluginTypeEnum.java | 8 ++++---- .../module/iot/enums/product/IotProductStatusEnum.java | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java index 11f8a6ef88..acbc992395 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.enums.plugin; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.Getter; import java.util.Arrays; @@ -11,12 +11,12 @@ import java.util.Arrays; * @author haohao */ @Getter -public enum IotPluginDeployTypeEnum implements IntArrayValuable { +public enum IotPluginDeployTypeEnum implements ArrayValuable { JAR(0, "JAR 部署"), STANDALONE(1, "独立部署"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray(); + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginDeployTypeEnum::getDeployType).toArray(Integer[]::new); /** * 部署方式 @@ -45,7 +45,7 @@ public enum IotPluginDeployTypeEnum implements IntArrayValuable { } @Override - public int[] array() { + public Integer[] array() { return ARRAYS; } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java index aee2fbe0f6..9b187c5b66 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.enums.plugin; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.Getter; import java.util.Arrays; @@ -11,12 +11,12 @@ import java.util.Arrays; * @author haohao */ @Getter -public enum IotPluginStatusEnum implements IntArrayValuable { +public enum IotPluginStatusEnum implements ArrayValuable { STOPPED(0, "停止"), RUNNING(1, "运行"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginStatusEnum::getStatus).toArray(); + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginStatusEnum::getStatus).toArray(Integer[]::new); /** * 状态 @@ -41,7 +41,7 @@ public enum IotPluginStatusEnum implements IntArrayValuable { } @Override - public int[] array() { + public Integer[] array() { return ARRAYS; } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java index 8b9cef2b46..0f81d5a8f7 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.enums.plugin; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; @@ -13,12 +13,12 @@ import java.util.Arrays; */ @AllArgsConstructor @Getter -public enum IotPluginTypeEnum implements IntArrayValuable { +public enum IotPluginTypeEnum implements ArrayValuable { NORMAL(0, "普通插件"), DEVICE(1, "设备插件"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginTypeEnum::getType).toArray(); + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginTypeEnum::getType).toArray(Integer[]::new); /** * 类型 @@ -31,7 +31,7 @@ public enum IotPluginTypeEnum implements IntArrayValuable { private final String name; @Override - public int[] array() { + public Integer[] array() { return ARRAYS; } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java index 4522906712..ee8be5c81e 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java @@ -18,7 +18,7 @@ public enum IotProductStatusEnum implements ArrayValuable { UNPUBLISHED(0, "开发中"), PUBLISHED(1, "已发布"); - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductStatusEnum::getType).toArray(Integer[]::new); + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductStatusEnum::getStatus).toArray(Integer[]::new); /** * 类型 From 5fbfe49305425ced4fc318daa9f37b6bcfa67507 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 28 Jan 2025 04:56:03 +0800 Subject: [PATCH 112/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91Bpm=EF=BC=9A=E8=AE=BE=E5=A4=87=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E4=B8=8A=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/api/device/IotDeviceUpstreamApi.java | 4 +- .../module/iot/enums/ErrorCodeConstants.java | 1 - .../device/IotDeviceMessageTypeEnum.java | 12 ++++- .../api/device/IoTDeviceUpstreamApiImpl.java | 8 +-- .../admin/device/IotDeviceController.java | 8 +++ .../admin/device/IotDeviceDataController.java | 16 ++---- .../IotDeviceSimulationReportReqVO.java | 30 +++++++++++ .../IotDeviceDataSimulatorSaveReqVO.java | 44 ---------------- .../plugin/UnifiedConfiguration.java | 6 ++- .../iot/service/device/IotDeviceService.java | 14 +++--- .../service/device/IotDeviceServiceImpl.java | 37 ++++++++++++++ .../device/data/IotDevicePropertyService.java | 8 --- .../data/IotDevicePropertyServiceImpl.java | 32 ------------ .../upstream/IotDeviceUpstreamService.java | 4 +- .../IotDeviceUpstreamServiceImpl.java | 4 +- .../common/api/DeviceDataApiClient.java | 4 +- .../plugin/http/service/HttpVertxHandler.java | 2 +- .../src/main/resources/application-local.yaml | 50 ++++++++++++------- 18 files changed, 147 insertions(+), 137 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSimulationReportReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index 996ff1f5a3..a7c28b8cbe 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -35,7 +35,7 @@ public interface IotDeviceUpstreamApi { * @param reportReqDTO 上报设备属性数据 DTO */ @PostMapping(PREFIX + "/report-property") - CommonResult reportDevicePropertyData(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); + CommonResult reportDeviceProperty(@Valid @RequestBody IotDevicePropertyReportReqDTO reportReqDTO); /** * 上报设备事件数据 @@ -43,6 +43,6 @@ public interface IotDeviceUpstreamApi { * @param reportReqDTO 设备事件 */ @PostMapping(PREFIX + "/report-event") - CommonResult reportDeviceEventData(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO); + CommonResult reportDeviceEvent(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 305aa6c7fd..4539f12591 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -30,7 +30,6 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); - ErrorCode DEVICE_DATA_CONTENT_JSON_PARSE_ERROR = new ErrorCode(1_050_003_007, "导入设备数据格式错误!"); // ========== 产品分类 1-050-004-000 ========== ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java index b3f00e8600..20140ead2e 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -1,22 +1,32 @@ package cn.iocoder.yudao.module.iot.enums.device; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.Getter; import lombok.RequiredArgsConstructor; +import java.util.Arrays; + /** * IoT 设备消息类型枚举 */ @Getter @RequiredArgsConstructor -public enum IotDeviceMessageTypeEnum { +public enum IotDeviceMessageTypeEnum implements ArrayValuable { STATE("state"), // 设备状态 PROPERTY("property"), // 设备属性 EVENT("event"); // 设备事件 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new); + /** * 属性 */ private final String type; + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index 0e6df14804..3ee3ec6a4f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -29,14 +29,14 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { } @Override - public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { - deviceUpstreamService.reportDevicePropertyData(reportReqDTO); + public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { + deviceUpstreamService.reportDeviceProperty(reportReqDTO); return success(true); } @Override - public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { - deviceUpstreamService.reportDeviceEventData(reportReqDTO); + public CommonResult reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { + deviceUpstreamService.reportDeviceEvent(reportReqDTO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index e0c214bc19..5080881acf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -159,4 +159,12 @@ public class IotDeviceController { ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list); } + @PostMapping("/simulation-report") + @Operation(summary = "模拟设备上报") + @PreAuthorize("@ss.hasPermission('iot:device:simulation-report')") + public CommonResult simulationReportDevice(@Valid @RequestBody IotDeviceSimulationReportReqVO simulatorReqVO) { + deviceService.simulationReportDevice(simulatorReqVO); + return success(true); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 8012ec2ec4..6c2ea7a2d2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import io.swagger.v3.oas.annotations.Operation; @@ -13,7 +13,9 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; @@ -47,16 +49,6 @@ public class IotDeviceDataController { return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); } - // TODO:功能权限 - @PostMapping("/simulator") - @Operation(summary = "模拟设备") - public CommonResult simulatorDevice(@Valid @RequestBody IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { - //TODO:先使用 IotDeviceDataSimulatorSaveReqVO 另外content里数据类型的效验前端也没做,后端应该要要效验一下,这块后续看看怎么安排 - // TODO @super:应该 deviceDataService 里面有个 simulatorDevice,然后里面去 insert 日志! - deviceDataService.simulatorSend(simulatorReqVO); - return success(true); - } - // TODO:功能权限 @GetMapping("/log/page") @Operation(summary = "获得设备日志分页") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSimulationReportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSimulationReportReqVO.java new file mode 100644 index 0000000000..aea4f9077d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSimulationReportReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 模拟设备上报 Request VO") // 属性上报、事件上报、状态变更等 +@Data +public class IotDeviceSimulationReportReqVO { + + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @NotNull(message = "设备编号不能为空") + private Long id; + + @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") + @NotEmpty(message = "消息类型不能为空") + @InEnum(IotDeviceMessageTypeEnum.class) + private String type; + + @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "report") + @NotEmpty(message = "标识符不能为空") + private String identifier; // 参见 IotDeviceMessageIdentifierEnum 枚举类 + + @Schema(description = "请求参数", requiredMode = Schema.RequiredMode.REQUIRED) + private Object data; // 例如说:属性上报的 properties、事件上报的 params + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java deleted file mode 100644 index efa608e572..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceDataSimulatorSaveReqVO.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -// TODO super: SaveReqVO => ReqVO -@Schema(description = "管理后台 - IoT 模拟设备数据 Request VO") -@Data -public class IotDeviceDataSimulatorSaveReqVO { - - // TODO @super:感觉后端随机更合适? - @Schema(description = "消息 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "msg123") - private String id; - - // TODO @super:不用传递 productKey,因为 deviceKey 可以推导出来 - // TODO 讨论: 日志记录的时候要记录一下productKey,目前是前端已经有productKey了,所以前端传入,如果不传入的话,后端要根据deviceKey查询productKey,感觉直传是不是效率高一些 - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "product123") - @NotEmpty(message = "产品标识不能为空") - private String productKey; - - @Schema(description = "设备标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device123") - @NotEmpty(message = "设备标识不能为空") - private String deviceKey; - - // TODO @super:type、subType,是不是不用传递,因为模拟只有属性??? - // TODO 讨论: 不只模拟属性 - @Schema(description = "消息/日志类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property") - @NotEmpty(message = "消息类型不能为空") - private String type; - - @Schema(description = "标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature") - @NotEmpty(message = "标识符不能为空") - private String subType; - - @Schema(description = "数据内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "{\"value\": 25.6}") - @NotEmpty(message = "数据内容不能为空") - private String content; - - // TODO @芋艿:需要讨论下,reportTime 到底以那个为准! - @Schema(description = "上报时间", requiredMode = Schema.RequiredMode.REQUIRED) - private Long reportTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java index e27d9b5fb3..150051ce58 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java @@ -7,6 +7,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.nio.file.Paths; + // TODO @芋艿:需要 review 下 @Slf4j @Configuration @@ -19,8 +21,8 @@ public class UnifiedConfiguration { // @DependsOn("deviceDataApiImpl") public SpringPluginManager pluginManager() { log.info("[init][实例化 SpringPluginManager]"); -// SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { - SpringPluginManager springPluginManager = new SpringPluginManager() { + SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { +// SpringPluginManager springPluginManager = new SpringPluginManager() { @Override public void startPlugins() { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 3efc4054fe..b5d690e513 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -1,13 +1,8 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportExcelVO; import jakarta.validation.Valid; import javax.annotation.Nullable; @@ -133,4 +128,11 @@ public interface IotDeviceService { */ IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport); + /** + * 模拟设备上报 + * + * @param reportReqVO 上报信息 + */ + void simulationReportDevice(IotDeviceSimulationReportReqVO reportReqVO); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 786e8e0ef0..8799080712 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -11,14 +11,17 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; +import cn.iocoder.yudao.module.iot.service.device.upstream.IotDeviceUpstreamService; import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import jakarta.validation.ConstraintViolationException; @@ -56,6 +59,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { @Resource @Lazy // 延迟加载,解决循环依赖 private IotDeviceGroupService deviceGroupService; + @Resource + @Lazy // 延迟加载,解决循环依赖 + private IotDeviceUpstreamService deviceUpstreamService; @Override public Long createDevice(IotDeviceSaveReqVO createReqVO) { @@ -84,6 +90,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2.1 转换 VO 为 DO // TODO @芋艿:state 相关的参数。另外,到底叫 state,还是 status 好! + // TODO @芋艿:各种 mqtt 是不是可以简化! IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> { o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); // 生成并设置必要的字段 @@ -392,6 +399,36 @@ public class IotDeviceServiceImpl implements IotDeviceService { return respVO; } + @Override + @SuppressWarnings("unchecked") + public void simulationReportDevice(IotDeviceSimulationReportReqVO reportReqVO) { + // 1. 校验存在 + IotDeviceDO device = validateDeviceExists(reportReqVO.getId()); + + // 2.1 情况一:属性上报 + String requestId = IdUtil.fastSimpleUUID(); + if (Objects.equals(reportReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { + deviceUpstreamService.reportDeviceProperty(IotDevicePropertyReportReqDTO.builder() + .requestId(requestId).reportTime(LocalDateTime.now()) + .productKey(device.getProductKey()).deviceName(device.getDeviceName()) + .properties((Map) reportReqVO.getData()).build()); + return; + } + // 2.2 情况二:事件上报 + if (Objects.equals(reportReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) { + // TODO 芋艿:待实现 + return; + } + // 2.3 情况三:状态变更 + if (Objects.equals(reportReqVO.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { + // TODO 芋艿:待实现 + updateDeviceStatus(new IotDeviceStatusUpdateReqVO().setId(device.getId()) + .setStatus((Integer) reportReqVO.getData())); + return; + } + throw new IllegalArgumentException("未知的类型:" + reportReqVO.getType()); + } + private void deleteDeviceCache(IotDeviceDO device) { // 保证在 @CacheEvict 之前,忽略租户 TenantUtils.executeIgnore(() -> getSelf().deleteDeviceCache0(device)); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java index b5fce2bc17..fbbc292cfc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import jakarta.validation.Valid; @@ -31,13 +30,6 @@ public interface IotDevicePropertyService { */ void saveDeviceProperty(IotDeviceMessage message); - /** - * 模拟设备 - * - * @param simulatorReqVO 设备数据 - */ - void simulatorSend(IotDeviceDataSimulatorSaveReqVO simulatorReqVO); - /** * 获得设备属性最新数据 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index 3f4c90b24f..0aa438b136 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -4,12 +4,9 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataSimulatorSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; @@ -34,9 +31,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DATA_CONTENT_JSON_PARSE_ERROR; /** * IoT 设备【属性】数据 Service 实现类 @@ -155,33 +150,6 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build())); } - //TODO @芋艿:copy 了 saveDeviceData 的逻辑,后续看看这块怎么优化 - @Override - public void simulatorSend(IotDeviceDataSimulatorSaveReqVO simulatorReqVO) { - // 1. 根据设备 key ,获得设备信息 - IotDeviceDO device = deviceService.getDeviceByDeviceKey(simulatorReqVO.getDeviceKey()); - - // 2. 解析 content 为 JSON 对象 - JSONObject contentJson; - try { - contentJson = JSONUtil.parseObj(simulatorReqVO.getContent()); - } catch (Exception e) { - throw exception(DEVICE_DATA_CONTENT_JSON_PARSE_ERROR); - } - - // TODO @芋艿:后续优化 - // 3. 构建物模型消息 -// IotDeviceMessage thingModelMessage = IotDeviceMessage.builder() -// .params(contentJson) // 将 content 作为 params -// .time(simulatorReqVO.getReportTime()) // 使用上报时间 -// .productKey(simulatorReqVO.getProductKey()) -// .deviceName(device.getDeviceName()) -// .build(); - - // 4. 发送模拟消息 -// simulateSendProducer.sendDeviceMessage(thingModelMessage); - } - @Override public List getLatestDeviceProperties(@Valid IotDeviceDataPageReqVO deviceDataReqVO) { // List list = new ArrayList<>(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java index 16d387d7b0..12fd8a9491 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamService.java @@ -25,13 +25,13 @@ public interface IotDeviceUpstreamService { * * @param reportReqDTO 上报设备属性数据 DTO */ - void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO); + void reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO); /** * 上报设备事件数据 * * @param reportReqDTO 设备事件 */ - void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO); + void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java index 4a456406ca..adfb1f83fd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/upstream/IotDeviceUpstreamServiceImpl.java @@ -43,7 +43,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { } @Override - public void reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + public void reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { // 1.1 获得设备 log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO); IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( @@ -65,7 +65,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { } @Override - public void reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + public void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { log.info("[reportDeviceEventData][上报设备事件数据: {}]", reportReqDTO); // TODO 芋艿:待实现 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java index a10e9ec3d5..ee68bf0342 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java @@ -35,13 +35,13 @@ public class DeviceDataApiClient implements IotDeviceUpstreamApi { } @Override - public CommonResult reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) { + public CommonResult reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { String url = deviceDataUrl + URL_PREFIX + "/report-event"; return doPost(url, reportReqDTO, "reportDeviceEventData"); } @Override - public CommonResult reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) { + public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { String url = deviceDataUrl + URL_PREFIX + "/report-property"; return doPost(url, reportReqDTO, "reportDevicePropertyData"); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java index ec4431d2d1..4ac85c2208 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java @@ -49,7 +49,7 @@ public class HttpVertxHandler implements Handler { .properties((Map) requestBody.asJsonObject().getMap().get("properties")) .build(); - deviceDataApi.reportDevicePropertyData(reportReqDTO); + deviceDataApi.reportDeviceProperty(reportReqDTO); ctx.response() .setStatusCode(200) diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index d3fb42b061..bbb94772c7 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,16 +45,16 @@ spring: primary: master datasource: master: - url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: root - password: 123456 + username: ruoyi-vue-pro + password: ruoyi-@h2ju02hebp # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -63,17 +63,25 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true + username: ruoyi-vue-pro + password: ruoyi-@h2ju02hebp + tdengine: # IOT 数据库 + # lazy: true # 开启懒加载,保证启动速度 + url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro + driver-class-name: com.taosdata.jdbc.rs.RestfulDriver username: root - password: 123456 + password: taosdata + druid: + validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: host: 127.0.0.1 # 地址 port: 6379 # 端口 - database: 0 # 数据库索引 -# password: dev # 密码,建议生产环境开启 + database: 1 # 数据库索引 + # password: 123456 # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### @@ -175,20 +183,22 @@ logging: cn.iocoder.yudao.module.crm.dal.mysql: debug cn.iocoder.yudao.module.erp.dal.mysql: debug cn.iocoder.yudao.module.iot.dal.mysql: debug + cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG cn.iocoder.yudao.module.ai.dal.mysql: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + com.taosdata: DEBUG # TDengine 的日志级别 debug: false --- #################### 微信公众号、小程序相关配置 #################### wx: mp: # 公众号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md 文档 - # app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) - # secret: 5abee519483bc9f8cb37ce280e814bd0 +# app-id: wx041349c6f39b268b # 测试号(牛希尧提供的) +# secret: 5abee519483bc9f8cb37ce280e814bd0 app-id: wx5b23ba7a5589ecbb # 测试号(自己的) secret: 2a7b3b20c537e52e74afd395eb85f61f - # app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) - # secret: bd4f9fab889591b62aeac0d7b8d8b4a0 +# app-id: wxa69ab825b163be19 # 测试号(Kongdy 提供的) +# secret: bd4f9fab889591b62aeac0d7b8d8b4a0 # 存储配置,解决 AccessToken 的跨节点的共享 config-storage: type: RedisTemplate # 采用 RedisTemplate 操作 Redis,会自动从 Spring 中获取 @@ -197,10 +207,10 @@ wx: miniapp: # 小程序配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-miniapp-spring-boot-starter/README.md 文档 # appid: wx62056c0d5e8db250 # 测试号(牛希尧提供的) # secret: 333ae72f41552af1e998fe1f54e1584a - # appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 - # secret: 6f270509224a7ae1296bbf1c8cb97aed - # appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) - # secret: 4a1a04e07f6a4a0751b39c3064a92c8b +# appid: wx63c280fe3248a3e7 # wenhualian的接口测试号 +# secret: 6f270509224a7ae1296bbf1c8cb97aed +# appid: wxc4598c446f8a9cb3 # 测试号(Kongdy 提供的) +# secret: 4a1a04e07f6a4a0751b39c3064a92c8b appid: wx66186af0759f47c9 # 测试号(puhui 提供的) secret: 3218bcbd112cbc614c7264ceb20144ac config-storage: @@ -260,7 +270,7 @@ justauth: iot: emq: # 账号 - username: anhaohao + username: haohao # 密码 password: ahh@123456 # 主机地址 @@ -272,4 +282,8 @@ iot: # 保持连接 keepalive: 60 # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) - clearSession: true \ No newline at end of file + clearSession: true + +pf4j: +# pluginsDir: /tmp/ + pluginsDir: ../plugins \ No newline at end of file From 6071afeae863e0e867db19a6b1b6ead10439bac5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 28 Jan 2025 08:35:07 +0800 Subject: [PATCH 113/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91Bpm=EF=BC=9A=E8=AE=BE=E5=A4=87=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E7=9A=84=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceDataController.java | 16 ++------ .../admin/device/IotDeviceLogController.java | 38 +++++++++++++++++++ .../vo/deviceData/IotDeviceLogPageReqVO.java | 15 +------- ...ataMapper.java => IotDeviceLogMapper.java} | 8 ++-- ...pper.java => IotDevicePropertyMapper.java} | 2 +- .../device/data/IotDeviceLogServiceImpl.java | 20 +++++----- .../data/IotDevicePropertyServiceImpl.java | 12 +++--- ...gDataMapper.xml => IotDeviceLogMapper.xml} | 29 ++------------ ...Mapper.xml => IotDevicePropertyMapper.xml} | 2 +- 9 files changed, 69 insertions(+), 73 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/{IotDeviceLogDataMapper.java => IotDeviceLogMapper.java} (86%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/{IotDevicePropertyDataMapper.java => IotDevicePropertyMapper.java} (99%) rename yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/{IotDeviceLogDataMapper.xml => IotDeviceLogMapper.xml} (59%) rename yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/{IotDevicePropertyDataMapper.xml => IotDevicePropertyMapper.xml} (99%) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java index 6c2ea7a2d2..c362e0bf1d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceDataController.java @@ -3,10 +3,10 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.*; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceDataRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotTimeDataRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; -import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -30,8 +30,6 @@ public class IotDeviceDataController { @Resource private IotDevicePropertyService deviceDataService; - @Resource - private IotDeviceLogService deviceLogDataService; // TODO @浩浩:这里的 /latest-list,包括方法名。 @GetMapping("/latest") @@ -49,12 +47,4 @@ public class IotDeviceDataController { return success(BeanUtils.toBean(list, IotTimeDataRespVO.class)); } - // TODO:功能权限 - @GetMapping("/log/page") - @Operation(summary = "获得设备日志分页") - public CommonResult> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) { - PageResult pageResult = deviceLogDataService.getDeviceLogPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, IotDeviceLogRespVO.class)); - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java new file mode 100644 index 0000000000..09a2301512 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; +import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 设备日志") +@RestController +@RequestMapping("/iot/device/log") +@Validated +public class IotDeviceLogController { + + @Resource + private IotDeviceLogService deviceLogService; + + // TODO:功能权限 + @GetMapping("/page") + @Operation(summary = "获得设备日志分页") + public CommonResult> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) { + PageResult pageResult = deviceLogService.getDeviceLogPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotDeviceLogRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java index a882a6d86a..0091c8197e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/deviceData/IotDeviceLogPageReqVO.java @@ -4,11 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - IoT 设备日志分页查询 Request VO") @Data @@ -18,16 +13,10 @@ public class IotDeviceLogPageReqVO extends PageParam { @NotEmpty(message = "设备标识不能为空") private String deviceKey; - // TODO @super:对应的枚举类 @Schema(description = "消息类型", example = "property") - private String type; + private String type; // 参见 IotDeviceMessageTypeEnum 枚举,精准匹配 @Schema(description = "标识符", example = "temperature") - // TODO @super:对应的枚举类 - private String subType; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; + private String identifier; // 参见 IotDeviceMessageIdentifierEnum 枚举,模糊匹配 } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java similarity index 86% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java index cd9f387a71..81a6220b85 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java @@ -4,18 +4,17 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDevi import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.util.List; - /** * 设备日志 {@link IotDeviceLogDO} Mapper 接口 */ @Mapper @TDengineDS @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 -public interface IotDeviceLogDataMapper { +public interface IotDeviceLogMapper { /** * 创建设备日志超级表 @@ -44,7 +43,8 @@ public interface IotDeviceLogDataMapper { * @param reqVO 分页查询条件 * @return 设备日志列表 */ - List selectPage(@Param("reqVO") IotDeviceLogPageReqVO reqVO); + IPage selectPage(IPage page, + @Param("reqVO") IotDeviceLogPageReqVO reqVO); /** * 获得设备日志总数 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java similarity index 99% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index ac212cac47..852adb256d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyDataMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -20,7 +20,7 @@ import java.util.stream.Collectors; @Mapper @TDengineDS @InterceptorIgnore(tenantLine = "true") // 避免 SQL 解析,因为 JSqlParser 对 TDengine 的 SQL 解析会报错 -public interface IotDevicePropertyDataMapper { +public interface IotDevicePropertyMapper { List getProductPropertySTableFieldList(@Param("productKey") String productKey); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java index fa5398a79a..64649c5d87 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -7,15 +7,15 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.deviceData.IotDeviceLogPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogDataMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.util.List; - /** * IoT 设备日志数据 Service 实现类 * @@ -27,17 +27,17 @@ import java.util.List; public class IotDeviceLogServiceImpl implements IotDeviceLogService { @Resource - private IotDeviceLogDataMapper deviceLogDataMapper; + private IotDeviceLogMapper deviceLogMapper; @Override public void defineDeviceLog() { - if (StrUtil.isNotEmpty(deviceLogDataMapper.showDeviceLogSTable())) { + if (StrUtil.isNotEmpty(deviceLogMapper.showDeviceLogSTable())) { log.info("[defineDeviceLog][设备日志超级表已存在,创建跳过]"); return; } log.info("[defineDeviceLog][设备日志超级表不存在,创建开始...]"); - deviceLogDataMapper.createDeviceLogSTable(); + deviceLogMapper.createDeviceLogSTable(); log.info("[defineDeviceLog][设备日志超级表不存在,创建成功]"); } @@ -46,15 +46,15 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { IotDeviceLogDO log = BeanUtils.toBean(message, IotDeviceLogDO.class) .setId(IdUtil.fastSimpleUUID()) .setContent(JsonUtils.toJsonString(message.getData())); - deviceLogDataMapper.insert(log); + deviceLogMapper.insert(log); } @Override public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { // TODO @芋艿:增加一个表不存在的 try catch - List list = deviceLogDataMapper.selectPage(pageReqVO); - Long total = deviceLogDataMapper.selectCount(pageReqVO); - return new PageResult<>(list, total); + IPage page = deviceLogMapper.selectPage( + new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); + return new PageResult<>(page.getRecords(), page.getTotal()); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index 0aa438b136..0b3a4f11b2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.dal.redis.device.DevicePropertyRedisDAO; -import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyDataMapper; +import cn.iocoder.yudao.module.iot.dal.tdengine.IotDevicePropertyMapper; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; @@ -68,7 +68,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { private DevicePropertyRedisDAO deviceDataRedisDAO; @Resource - private IotDevicePropertyDataMapper devicePropertyDataMapper; + private IotDevicePropertyMapper devicePropertyMapper; @Override public void defineDevicePropertyData(Long productId) { @@ -79,7 +79,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { // 1.2 解析 DB 里的字段 List oldFields = new ArrayList<>(); try { - oldFields.addAll(devicePropertyDataMapper.getProductPropertySTableFieldList(product.getProductKey())); + oldFields.addAll(devicePropertyMapper.getProductPropertySTableFieldList(product.getProductKey())); } catch (Exception e) { if (!e.getMessage().contains("Table does not exist")) { throw e; @@ -93,11 +93,11 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { log.info("[defineDevicePropertyData][productId({}) 没有需要定义的属性]", productId); return; } - devicePropertyDataMapper.createProductPropertySTable(product.getProductKey(), newFields); + devicePropertyMapper.createProductPropertySTable(product.getProductKey(), newFields); return; } // 2.2 情况二:如果是修改的时候,需要更新表 - devicePropertyDataMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); + devicePropertyMapper.alterProductPropertySTable(product.getProductKey(), oldFields, newFields); } private List buildTableFieldList(List thingModels) { @@ -142,7 +142,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } // 3.1 保存设备属性【数据】 - devicePropertyDataMapper.insert(device, properties, + devicePropertyMapper.insert(device, properties, LocalDateTimeUtil.toEpochMilli(message.getReportTime())); // TODO @芋艿:后续要看看,查询的时候,能不能用 LocalDateTime // 3.2 保存设备属性【日志】 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml similarity index 59% rename from yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml rename to yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml index bbc1fb7185..2ff555da9f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml @@ -2,7 +2,7 @@ - + CREATE STABLE IF NOT EXISTS device_log ( @@ -40,38 +40,17 @@ - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml similarity index 99% rename from yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml rename to yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml index 21d1ed16e9..87f21b1829 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyDataMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml @@ -2,7 +2,7 @@ - + DESCRIBE product_property_${productKey} - - + SELECT ${reqVO.identifier} AS `value`, report_time AS update_time + FROM device_property_${reqVO.deviceKey} + WHERE ${reqVO.identifier} IS NOT NULL ORDER BY ts DESC - LIMIT #{params.rows} OFFSET #{params.page} - - - - \ No newline at end of file From 0b16f1678cc02aa5310e5820219a9ce16ea79ecd Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 28 Jan 2025 12:04:59 +0800 Subject: [PATCH 116/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91Bpm=EF=BC=9A=E5=AE=8C=E5=96=84=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=B1=9E=E6=80=A7=E7=9A=84=E5=8E=86=E5=8F=B2=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceLogController.java | 3 +- .../device/IotDevicePropertyController.java | 34 +++++++-- .../IotDevicePropertyHistoryPageReqVO.java | 35 ++++++++++ .../vo/data/IotDevicePropertyPageReqVO.java | 25 ------- .../vo/data/IotDevicePropertyRespVO.java | 2 +- .../thingmodel/model/ThingModelProperty.java | 5 +- .../convert/device/IotDeviceDataConvert.java | 32 --------- .../module/iot/convert/package-info.java | 3 + .../dal/tdengine/IotDevicePropertyMapper.java | 4 +- .../device/data/IotDeviceLogServiceImpl.java | 14 ++-- .../device/data/IotDevicePropertyService.java | 6 +- .../data/IotDevicePropertyServiceImpl.java | 26 ++++--- .../module/iot/util/IotTdDatabaseUtils.java | 69 ------------------- .../mapper/device/IotDevicePropertyMapper.xml | 7 +- 14 files changed, 103 insertions(+), 162 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyHistoryPageReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyPageReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java index 99960dfa42..81d1bff945 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceLogController.java @@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -27,9 +28,9 @@ public class IotDeviceLogController { @Resource private IotDeviceLogService deviceLogService; - // TODO:功能权限 @GetMapping("/page") @Operation(summary = "获得设备日志分页") + @PreAuthorize("@ss.hasPermission('iot:device:log-query')") public CommonResult> getDeviceLogPage(@Valid IotDeviceLogPageReqVO pageReqVO) { PageResult pageResult = deviceLogService.getDeviceLogPage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotDeviceLogRespVO.class)); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java index 6cd5448fa7..d8021d2e89 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDevicePropertyController.java @@ -3,10 +3,11 @@ package cn.iocoder.yudao.module.iot.controller.admin.device; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; @@ -15,12 +16,16 @@ import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @@ -42,14 +47,22 @@ public class IotDevicePropertyController { @Resource private IotDeviceService deviceService; - // TODO @芋艿:权限 @GetMapping("/latest") @Operation(summary = "获取设备属性最新属性") - public CommonResult> getLatestDeviceProperties(@Valid IotDevicePropertyPageReqVO pageReqVO) { - Map properties = devicePropertyService.getLatestDeviceProperties(pageReqVO); + @Parameters({ + @Parameter(name = "deviceId", description = "设备编号", required = true), + @Parameter(name = "identifier", description = "标识符"), + @Parameter(name = "name", description = "名称") + }) + @PreAuthorize("@ss.hasPermission('iot:device:property-query')") + public CommonResult> getLatestDeviceProperties( + @RequestParam("deviceId") Long deviceId, + @RequestParam(value = "identifier", required = false) String identifier, + @RequestParam(value = "name", required = false) String name) { + Map properties = devicePropertyService.getLatestDeviceProperties(deviceId); // 拼接数据 - IotDeviceDO device = deviceService.getDevice(pageReqVO.getDeviceId()); + IotDeviceDO device = deviceService.getDevice(deviceId); Assert.notNull(device, "设备不存在"); List thingModels = thingModelService.getThingModelListByProductId(device.getProductId()); return success(convertList(properties.entrySet(), entry -> { @@ -58,6 +71,13 @@ public class IotDevicePropertyController { if (thingModel == null || thingModel.getProperty() == null) { return null; } + if (StrUtil.isNotEmpty(identifier) && !StrUtil.contains(thingModel.getIdentifier(), identifier)) { + return null; + } + if (StrUtil.isNotEmpty(name) && !StrUtil.contains(thingModel.getName(), name)) { + return null; + } + // 构建对象 IotDevicePropertyDO property = entry.getValue(); return BeanUtils.toBean(thingModel, IotDevicePropertyRespVO.class) .setDataType(thingModel.getProperty().getDataType()) @@ -66,11 +86,11 @@ public class IotDevicePropertyController { })); } - // TODO @芋艿:权限 @GetMapping("/history-page") @Operation(summary = "获取设备属性历史数据") + @PreAuthorize("@ss.hasPermission('iot:device:property-query')") public CommonResult> getHistoryDevicePropertyPage( - @Valid IotDevicePropertyPageReqVO pageReqVO) { + @Valid IotDevicePropertyHistoryPageReqVO pageReqVO) { Assert.notEmpty(pageReqVO.getIdentifier(), "标识符不能为空"); return success(devicePropertyService.getHistoryDevicePropertyPage(pageReqVO)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyHistoryPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyHistoryPageReqVO.java new file mode 100644 index 0000000000..0de45e4a71 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyHistoryPageReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.data; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 设备属性历史分页 Request VO") +@Data +public class IotDevicePropertyHistoryPageReqVO extends PageParam { + + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @NotNull(message = "设备编号不能为空") + private Long deviceId; + + @Schema(description = "设备 Key", hidden = true) + private String deviceKey; // 非前端传递,后端自己查询设置 + + @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "属性标识符不能为空") + private String identifier; + + @Schema(description = "时间范围", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Size(min = 2, max = 2, message = "请选择时间范围") + private LocalDateTime[] times; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyPageReqVO.java deleted file mode 100644 index 9d391ddbc5..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyPageReqVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.device.vo.data; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - IoT 设备属性 Request VO") -@Data -public class IotDevicePropertyPageReqVO extends PageParam { - - @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") - @NotNull(message = "设备编号不能为空") - private Long deviceId; - - @Schema(description = "设备 Key", hidden = true) - private String deviceKey; // 非前端传递,后端自己查询设置 - - @Schema(description = "属性标识符", requiredMode = Schema.RequiredMode.REQUIRED) - private String identifier; - - @Schema(description = "属性名称", requiredMode = Schema.RequiredMode.REQUIRED) - private String name; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyRespVO.java index 93d33bf496..3a3fa49026 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/data/IotDevicePropertyRespVO.java @@ -14,7 +14,7 @@ public class IotDevicePropertyRespVO { private Object value; @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) - private Long updateTime; + private Long updateTime; // 由于从 TDengine 查询出来的是 Long 类型,所以这里也使用 Long 类型 // ========== 基于 ThingModel 查询 ========== diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index db7333df25..4c131d801b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -31,10 +31,7 @@ public class ThingModelProperty { */ private String accessMode; /** - * 是否是标准品类的必选服务。 - * - * - true:是 - * - false:否 + * 是否是标准品类的必选服务 */ private Boolean required; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java deleted file mode 100644 index d8f6e47042..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/device/IotDeviceDataConvert.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.iot.convert.device; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -// TODO 是否要删除? -@Mapper -public interface IotDeviceDataConvert { - - IotDeviceDataConvert INSTANCE = Mappers.getMapper(IotDeviceDataConvert.class); - -// default List convert(Map deviceData, IotDeviceDO device){ -// List list = new ArrayList<>(); -// deviceData.forEach((identifier, value) -> { -//// ThingModelProperty property = ThingModelService.INSTANCE.getProperty(device.getProductId(), identifier); -//// if (Objects.isNull(property)) { -//// return; -//// } -// IotDeviceDataRespVO vo = new IotDeviceDataRespVO(); -// vo.setDeviceId(device.getId()); -// vo.setProductKey(device.getProductKey()); -// vo.setDeviceName(device.getDeviceName()); -// vo.setIdentifier(identifier); -//// vo.setName(property.getName()); -//// vo.setDataType(property.getDataType().getType()); -// vo.setUpdateTime(device.getUpdateTime()); -// vo.setValue(value.toString()); -// list.add(vo); -// }); -// return list; -// } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java index c196c25c3d..28131f0905 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java @@ -1 +1,4 @@ +/** + * TODO 芋艿:占位 + */ package cn.iocoder.yudao.module.iot.convert; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index cb39ef51dc..8ca8bcecbb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.TDengineTableField; @@ -85,6 +85,6 @@ public interface IotDevicePropertyMapper { @Param("reportTime") Long reportTime); IPage selectPageByHistory(IPage page, - @Param("reqVO") IotDevicePropertyPageReqVO reqVO); + @Param("reqVO") IotDevicePropertyHistoryPageReqVO reqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java index bfa604d027..79bb0a52a8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -51,10 +51,16 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { @Override public PageResult getDeviceLogPage(IotDeviceLogPageReqVO pageReqVO) { - // TODO @芋艿:增加一个表不存在的 try catch - IPage page = deviceLogMapper.selectPage( - new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); - return new PageResult<>(page.getRecords(), page.getTotal()); + try { + IPage page = deviceLogMapper.selectPage( + new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); + return new PageResult<>(page.getRecords(), page.getTotal()); + } catch (Exception exception) { + if (exception.getMessage().contains("Table does not exist")) { + return PageResult.empty(); + } + throw exception; + } } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java index 7d856296e0..0711230aab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; @@ -36,7 +36,7 @@ public interface IotDevicePropertyService { * @param deviceId 设备编号 * @return 设备属性最新数据 */ - Map getLatestDeviceProperties(@Valid IotDevicePropertyPageReqVO deviceId); + Map getLatestDeviceProperties(Long deviceId); /** * 获得设备属性历史数据 @@ -44,6 +44,6 @@ public interface IotDevicePropertyService { * @param pageReqVO 分页请求 * @return 设备属性历史数据 */ - PageResult getHistoryDevicePropertyPage(@Valid IotDevicePropertyPageReqVO pageReqVO); + PageResult getHistoryDevicePropertyPage(@Valid IotDevicePropertyHistoryPageReqVO pageReqVO); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index d3df806d3a..c4a3254c8c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -6,7 +6,7 @@ import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyHistoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDevicePropertyRespVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDateOrTextDataSpecs; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -25,7 +25,6 @@ import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jakarta.annotation.Resource; -import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -146,33 +145,38 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { // 3.1 保存设备属性【数据】 devicePropertyMapper.insert(device, properties, - LocalDateTimeUtil.toEpochMilli(message.getReportTime())); // TODO @芋艿:后续要看看,查询的时候,能不能用 LocalDateTime + LocalDateTimeUtil.toEpochMilli(message.getReportTime())); // 3.2 保存设备属性【日志】 deviceDataRedisDAO.set(message.getDeviceKey(), convertMap(properties.entrySet(), Map.Entry::getKey, entry -> IotDevicePropertyDO.builder().value(entry.getValue()).updateTime(message.getReportTime()).build())); } - // TODO @芋艿:需要在优化下,根据 name 之类的过滤 @Override - public Map getLatestDeviceProperties(@Valid IotDevicePropertyPageReqVO pageReqVO) { + public Map getLatestDeviceProperties(Long deviceId) { // 获取设备信息 - IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId()); + IotDeviceDO device = deviceService.validateDeviceExists(deviceId); // 获得设备属性 return deviceDataRedisDAO.get(device.getDeviceKey()); } @Override - public PageResult getHistoryDevicePropertyPage(IotDevicePropertyPageReqVO pageReqVO) { + public PageResult getHistoryDevicePropertyPage(IotDevicePropertyHistoryPageReqVO pageReqVO) { // 获取设备信息 IotDeviceDO device = deviceService.validateDeviceExists(pageReqVO.getDeviceId()); pageReqVO.setDeviceKey(device.getDeviceKey()); - // TODO @芋艿:增加一个表不存在的 try catch - IPage page = devicePropertyMapper.selectPageByHistory( - new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); - return new PageResult<>(page.getRecords(), page.getTotal()); + try { + IPage page = devicePropertyMapper.selectPageByHistory( + new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()), pageReqVO); + return new PageResult<>(page.getRecords(), page.getTotal()); + } catch (Exception exception) { + if (exception.getMessage().contains("Table does not exist")) { + return PageResult.empty(); + } + throw exception; + } } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java deleted file mode 100644 index a409c80692..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/IotTdDatabaseUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.iot.util; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.enums.IotConstants; -import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; - -// TODO @芋艿:可能要思索下,有没更好的处理方式 -// TODO @芋艿:怎么改成无状态 -/** - * TD 数据库工具类 - * - * @author AlwaysSuper - */ -public class IotTdDatabaseUtils { - - /** - * 获取数据库名称 - */ - public static String getDatabaseName(String url) { -// TODO @alwayssuper:StrUtil.subAfter("/") - return StrUtil.subAfter(url, "/", true); - } - - /** - * 获取产品超级表表名 - * - * @param deviceType 设备类型 - * @param productKey 产品 Key - * @return 产品超级表表名 - */ - public static String getProductSuperTableName(Integer deviceType, String productKey) { - Assert.notNull(deviceType, "deviceType 不能为空"); - if (IotProductDeviceTypeEnum.GATEWAY_SUB.getType().equals(deviceType)) { - return String.format(IotConstants.GATEWAY_SUB_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } - if (IotProductDeviceTypeEnum.GATEWAY.getType().equals(deviceType)) { - return String.format(IotConstants.GATEWAY_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } - if (IotProductDeviceTypeEnum.DIRECT.getType().equals(deviceType)){ - return String.format(IotConstants.DEVICE_STABLE_NAME_FORMAT, productKey).toLowerCase(); - } - throw new IllegalArgumentException("deviceType 不正确"); - } - - /** - * 获取物模型日志超级表表名 - * - * @param productKey 产品 Key - * @return 物模型日志超级表表名 - * - */ - public static String getThingModelMessageSuperTableName(String productKey) { - return "thing_model_message_" + productKey.toLowerCase(); - } - - /** - * 获取物模型日志设备表名 - * - * @param productKey 产品 Key - * @param deviceName 设备名称 - * @return 物模型日志设备表名 - */ - public static String getThingModelMessageDeviceTableName(String productKey, String deviceName) { - return String.format(IotConstants.THING_MODEL_MESSAGE_TABLE_NAME_FORMAT, - productKey.toLowerCase(), deviceName.toLowerCase()); - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml index 696b23a1be..7a3c3eb6f8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml @@ -66,11 +66,12 @@ DESCRIBE product_property_${productKey} - From 76ab64a255c311a354060eb2580f57a37aa29b7f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 28 Jan 2025 22:24:28 +0800 Subject: [PATCH 117/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E7=89=A9=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=9A=84=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingmodel/model/ThingModelEvent.java | 1 + .../thingmodel/model/ThingModelParam.java | 1 + .../thingmodel/model/ThingModelProperty.java | 1 + .../thingmodel/model/ThingModelRespVO.java | 50 ------------------- .../thingmodel/model/ThingModelService.java | 1 + .../thingmodel/vo/IotThingModelListReqVO.java | 11 ++-- .../thingmodel/vo/IotThingModelPageReqVO.java | 9 ++-- .../thingmodel/vo/IotThingModelRespVO.java | 1 + .../thingmodel/vo/IotThingModelSaveReqVO.java | 1 + .../thingmodel/IotThingModelService.java | 1 + .../thingmodel/IotThingModelServiceImpl.java | 48 +++++++++--------- 11 files changed, 42 insertions(+), 83 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 0ed5d29d1a..0a5e0056f0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -5,6 +5,7 @@ import lombok.Data; import java.util.List; +// TODO @puhui999:必要的参数校验 /** * 物模型中的事件 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java index 9cddf1d290..6215d1537c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -6,6 +6,7 @@ import lombok.Data; import java.util.List; +// TODO @puhui999:必要的参数校验 /** * IOT 产品物模型中的参数 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 4c131d801b..157a4c4889 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -6,6 +6,7 @@ import lombok.Data; import java.util.List; +// TODO @puhui999:必要的参数校验 /** * 物模型中的属性 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java deleted file mode 100644 index 44fd24c0ad..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelRespVO.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; - -import lombok.*; - -import java.util.List; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@ToString -public class ThingModelRespVO { - - /** - * 产品编号 - */ - private Long id; - - /** - * 产品标识 - */ - private String productKey; - - /** - * 物模型 - */ - private Model model; - - /** - * 物模型 - */ - @Data - public static class Model { - - /** - * 属性列表 - */ - private List properties; - - /** - * 服务列表 - */ - private List services; - - /** - * 事件列表 - */ - private List events; - } -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 6803c7103c..067ca2ea1e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -5,6 +5,7 @@ import lombok.Data; import java.util.List; +// TODO @puhui999:必要的参数校验 /** * 物模型中的服务 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java index dd77cfc5ee..8f7f374dd0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java @@ -6,10 +6,15 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; -@Schema(description = "管理后台 - IoT 产品物模型List Request VO") +// TODO @puhui999:部分字段,可以用 cursor 加上 example +@Schema(description = "管理后台 - IoT 产品物模型 List Request VO") @Data public class IotThingModelListReqVO { + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "产品编号不能为空") + private Long productId; + @Schema(description = "功能标识") private String identifier; @@ -20,8 +25,4 @@ public class IotThingModelListReqVO { @InEnum(IotThingModelTypeEnum.class) private Integer type; - @Schema(description = "产品 ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "产品 ID 不能为空") - private Long productId; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java index 314441fb62..404bc7cc24 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java @@ -9,12 +9,17 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class IotThingModelPageReqVO extends PageParam { + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "产品编号不能为空") + private Long productId; + @Schema(description = "功能标识") private String identifier; @@ -25,8 +30,4 @@ public class IotThingModelPageReqVO extends PageParam { @InEnum(IotThingModelTypeEnum.class) private Integer type; - @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "产品ID不能为空") - private Long productId; - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java index 88c197babb..37a42edc9e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java @@ -10,6 +10,7 @@ import lombok.Data; import java.time.LocalDateTime; +// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java index 0eb37231bd..18b8f40429 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java @@ -10,6 +10,7 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; +// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data public class IotThingModelSaveReqVO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index cc81e5f02f..92aa5978cf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -79,4 +79,5 @@ public interface IotThingModelService { * @return 产品物模型列表 */ List getThingModelList(IotThingModelListReqVO reqVO); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index 5c9f5fd051..5c97739018 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.thingmodel; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -54,29 +55,26 @@ public class IotThingModelServiceImpl implements IotThingModelService { @Override @Transactional(rollbackFor = Exception.class) public Long createThingModel(IotThingModelSaveReqVO createReqVO) { - // 1. 校验功能标识符在同一产品下是否唯一 + // 1.1 校验功能标识符在同一产品下是否唯一 validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); - - // 2. 功能名称在同一产品下是否唯一 + // 1.2 功能名称在同一产品下是否唯一 validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); - - // 3. 系统保留字段,不能用于标识符定义 + // 1.3 系统保留字段,不能用于标识符定义 validateNotDefaultEventAndService(createReqVO.getIdentifier()); - - // 4. 校验产品状态,发布状态下,不允许新增功能 + // 1.4 校验产品状态,发布状态下,不允许新增功能 validateProductStatus(createReqVO.getProductId()); - // 5. 插入数据库 + // 2. 插入数据库 IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(createReqVO); thingModelMapper.insert(thingModel); - // 6. 如果创建的是属性,需要更新默认的事件和服务 + // 3. 如果创建的是属性,需要更新默认的事件和服务 if (Objects.equals(createReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? - // 7. 删除缓存 + // 4. 删除缓存 deleteThingModelListCache(createReqVO.getProductKey()); return thingModel.getId(); } @@ -84,38 +82,35 @@ public class IotThingModelServiceImpl implements IotThingModelService { @Override @Transactional(rollbackFor = Exception.class) public void updateThingModel(IotThingModelSaveReqVO updateReqVO) { - // 1. 校验功能是否存在 + // 1.1 校验功能是否存在 validateProductThingModelMapperExists(updateReqVO.getId()); - - // 2. 校验功能标识符是否唯一 + // 1.2 校验功能标识符是否唯一 validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); - - // 3. 校验产品状态,发布状态下,不允许操作功能 + // 1.3 校验产品状态,发布状态下,不允许操作功能 validateProductStatus(updateReqVO.getProductId()); - // 4. 更新数据库 + // 2. 更新数据库 IotThingModelDO thingModel = IotThingModelConvert.INSTANCE.convert(updateReqVO); thingModelMapper.updateById(thingModel); - // 5. 如果更新的是属性,需要更新默认的事件和服务 + // 3. 如果更新的是属性,需要更新默认的事件和服务 if (Objects.equals(updateReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(updateReqVO.getProductId(), updateReqVO.getProductKey()); } - // 6. 删除缓存 + // 4. 删除缓存 deleteThingModelListCache(updateReqVO.getProductKey()); } @Override @Transactional(rollbackFor = Exception.class) public void deleteThingModel(Long id) { - // 1. 校验功能是否存在 + // 1.1 校验功能是否存在 IotThingModelDO thingModel = thingModelMapper.selectById(id); if (thingModel == null) { throw exception(THING_MODEL_NOT_EXISTS); } - - // 3. 校验产品状态,发布状态下,不允许操作功能 + // 1.2 校验产品状态,发布状态下,不允许操作功能 validateProductStatus(thingModel.getProductId()); // 2. 删除功能 @@ -168,6 +163,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { } } + // TODO @puhui999:这个方法,和 validateIdentifierUnique 可以融合下 private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { @@ -176,15 +172,16 @@ public class IotThingModelServiceImpl implements IotThingModelService { } private void validateProductStatus(Long createReqVO) { - IotProductDO product = productService.getProduct(createReqVO); + IotProductDO product = productService.validateProductExists(createReqVO); if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) { throw exception(PRODUCT_STATUS_NOT_ALLOW_THING_MODEL); } } + // TODO @芋艿:在 review 下 private void validateNotDefaultEventAndService(String identifier) { - // set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义 - if (CollUtil.containsAny(Arrays.asList("set", "get", "post", "property", "event", "time", "value"), Collections.singletonList(identifier))) { + // 系统保留字段,不能用于标识符定义 + if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { throw exception(THING_MODEL_IDENTIFIER_INVALID); } } @@ -205,6 +202,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { /** * 创建默认的事件和服务 + * + * @param productId 产品编号 + * @param productKey 产品标识 */ public void createDefaultEventsAndServices(Long productId, String productKey) { // 1. 获取当前属性列表 From f14cc470aacfc3fcd087dfd19259d230a1d2e60c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 28 Jan 2025 23:16:30 +0800 Subject: [PATCH 118/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91IoT=EF=BC=9A=E8=A7=A3=E5=86=B3=E7=89=A9?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E7=9A=84=20identifier=20=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E6=83=85=E5=86=B5=E4=B8=8B=EF=BC=8C=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E6=8F=92=E5=85=A5=E5=92=8C=E6=9F=A5=E8=AF=A2=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/dal/tdengine/IotDevicePropertyMapper.java | 2 +- .../resources/mapper/device/IotDevicePropertyMapper.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index 8ca8bcecbb..f2d6af97e9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -33,7 +33,7 @@ public interface IotDevicePropertyMapper { List oldFields, List newFields) { oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), - TDengineTableField.FIELD_TS, "device_key", "report_time")); + TDengineTableField.FIELD_TS, "report_time")); List addFields = newFields.stream().filter( // 新增的字段 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) .collect(Collectors.toList()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml index 7a3c3eb6f8..bdc40e8330 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml @@ -51,7 +51,7 @@ TAGS ('${device.deviceKey}') (ts, report_time, - ${key} + ${@cn.hutool.core.util.StrUtil@toUnderlineCase(key)} ) VALUES @@ -67,9 +67,9 @@ - INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, report_time) + INSERT INTO device_log_${deviceKey} (ts, id, product_key, device_name, type, identifier, content, code, report_time) USING device_log TAGS ('${deviceKey}') VALUES ( @@ -35,6 +36,7 @@ #{type}, #{identifier}, #{content}, + #{code}, #{reportTime} ) diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java index 3c6fee7a44..127332cc7f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java @@ -14,10 +14,28 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe */ public interface IotDeviceDownstreamHandler { + /** + * 调用设备服务 + * + * @param invokeReqDTO 调用设备服务的请求 + * @return 是否成功 + */ CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO); + /** + * 获取设备属性 + * + * @param getReqDTO 获取设备属性的请求 + * @return 是否成功 + */ CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO); + /** + * 设置设备属性 + * + * @param setReqDTO 设置设备属性的请求 + * @return 是否成功 + */ CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java index 27e1ff8d76..22ae7012d8 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java @@ -1,10 +1,18 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import io.vertx.core.json.JsonObject; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; @Slf4j @RequiredArgsConstructor @@ -15,9 +23,34 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler params = (Map) body.getMap().get("params"); + invokeReqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setIdentifier(identifier).setParams(params); + } catch (Exception e) { + log.error("[handle][解析参数失败]", e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用下游处理器 + try { + CommonResult result = deviceDownstreamHandler.invokeDeviceService(invokeReqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 服务调用异常]", invokeReqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java index d44a4e02e7..f93717386e 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java @@ -3,6 +3,11 @@ package cn.iocoder.yudao.module.iot.plugin.common.util; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.system.SystemUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import io.vertx.core.http.HttpHeaders; +import io.vertx.ext.web.RoutingContext; +import org.springframework.http.MediaType; /** * IoT 插件的通用工具类 @@ -28,4 +33,12 @@ public class IotPluginCommonUtils { SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID(), IdUtil.fastSimpleUUID()); } + @SuppressWarnings("deprecation") + public static void writeJson(RoutingContext routingContext, CommonResult result) { + routingContext.response() + .setStatusCode(200) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .end(JsonUtils.toJsonString(result)); + } + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java index 388ad8ac41..4e402f91fd 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.http.downstream.IotDeviceDownstreamHandlerImpl; import cn.iocoder.yudao.module.iot.plugin.http.upstream.IotDeviceUpstreamServer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -26,4 +28,9 @@ public class IotPluginHttpAutoConfiguration { return new IotDeviceUpstreamServer(port, deviceUpstreamApi); } + @Bean + public IotDeviceDownstreamHandler deviceDownstreamHandler() { + return new IotDeviceDownstreamHandlerImpl(); + } + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java index 816a172537..b70052530c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java @@ -5,7 +5,8 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePr import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; -import org.springframework.stereotype.Component; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; /** * HTTP 插件的 {@link IotDeviceDownstreamHandler} 实现类 @@ -15,28 +16,21 @@ import org.springframework.stereotype.Component; * * @author 芋道源码 */ -@Component // TODO @芋艿:后续统一处理 public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { @Override public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { - // TODO @芋艿:待实现 - System.out.println(); - return null; + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持调用设备服务"); } @Override public CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { - // TODO @芋艿:待实现 - System.out.println(); - return null; + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持获取设备属性"); } @Override public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { - // TODO @芋艿:待实现 - System.out.println(); - return null; + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); } } From 45b8172a617a15f5a7b34347ed1da208097a2339 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 31 Jan 2025 17:51:39 +0800 Subject: [PATCH 134/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=AE=9E=E7=8E=B0=20device=20?= =?UTF-8?q?=E4=B8=8B=E8=A1=8C=E5=B1=9E=E6=80=A7=E8=8E=B7=E5=8F=96=E3=80=81?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=9A=84=E4=B8=8B=E8=A1=8C=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceController.http | 30 +++++++- .../IotDeviceDownstreamServiceImpl.java | 73 +++++++++++++++++-- .../downstream/IotDeviceDownstreamServer.java | 10 ++- .../IotDevicePropertyGetVertxHandler.java | 61 ++++++++++++++++ .../IotDevicePropertySetVertxHandler.java | 61 ++++++++++++++++ .../IotDeviceServiceInvokeVertxHandler.java | 17 +++-- 6 files changed, 237 insertions(+), 15 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http index 9518f10aa3..34df37e93b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http @@ -1,4 +1,4 @@ -### 请求 /iot/device/simulation-downstream 接口 => 成功 +### 请求 /iot/device/simulation-downstream 接口(服务调用) => 成功 POST {{baseUrl}}/iot/device/simulation-downstream Content-Type: application/json tenant-id: {{adminTenentId}} @@ -11,4 +11,32 @@ Authorization: Bearer {{token}} "data": { "xx": "yy" } +} + +### 请求 /iot/device/simulation-downstream 接口(属性设置) => 成功 +POST {{baseUrl}}/iot/device/simulation-downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "property", + "identifier": "set", + "data": { + "xx": "yy" + } +} + +### 请求 /iot/device/simulation-downstream 接口(属性获取) => 成功 +POST {{baseUrl}}/iot/device/simulation-downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "property", + "identifier": "get", + "data": ["xx", "yy"] } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 4fa7e45a96..7f341f57c3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -6,6 +6,8 @@ import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceDownstreamAbstractReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -24,6 +26,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.client.RestTemplate; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -91,11 +94,12 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic */ @SuppressWarnings("unchecked") private void invokeDeviceService(IotDeviceSimulationDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); } + // TODO @super:【可优化】过滤掉不合法的服务 // 2. 发送请求 String url = String.format( "sys/%s/%s/thing/service/%s", @@ -125,16 +129,73 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param device 设备 * @param parentDevice 父设备 */ + @SuppressWarnings("unchecked") private void setDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - // TODO 1. 请求 + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数校验 + if (!(downstreamReqVO.getData() instanceof Map)) { + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); + } + // TODO @super:【可优化】过滤掉不合法的属性 - // TODO 2. 发送消息 + // 2. 发送请求 + String url = String.format( "sys/%s/%s/thing/service/property/set", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); + IotDevicePropertySetReqDTO reqDTO = new IotDevicePropertySetReqDTO() + .setProperties((Map) downstreamReqVO.getData()); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()) + .setData(reqDTO.getProperties()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[setDeviceProperty][设备({})属性设置失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } } + /** + * 获取设备属性 + * + * @param downstreamReqVO 下行请求 + * @param device 设备 + * @param parentDevice 父设备 + */ + @SuppressWarnings("unchecked") private void getDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { - // TODO 芋艿:这里需要获取设备属性 + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数校验 + if (!(downstreamReqVO.getData() instanceof List)) { + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型"); + } + // TODO @super:【可优化】过滤掉不合法的属性 + + // 2. 发送请求 + String url = String.format( "sys/%s/%s/thing/service/property/get", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); + IotDevicePropertyGetReqDTO reqDTO = new IotDevicePropertyGetReqDTO() + .setIdentifiers((List) downstreamReqVO.getData()); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.PROPERTY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()) + .setData(reqDTO.getIdentifiers()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[getDeviceProperty][设备({})属性获取失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } } /** diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java index b87a2fc9db..4c45e972e5 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertyGetVertxHandler; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertySetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceServiceInvokeVertxHandler; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; @@ -24,8 +26,12 @@ public class IotDeviceDownstreamServer { // 创建 Router 实例 Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); // 处理 Body - router.post(IotDeviceServiceInvokeVertxHandler.PATH).handler( - new IotDeviceServiceInvokeVertxHandler(deviceDownstreamHandler)); // 处理 Service Invoke + router.post(IotDeviceServiceInvokeVertxHandler.PATH) + .handler(new IotDeviceServiceInvokeVertxHandler(deviceDownstreamHandler)); + router.post(IotDevicePropertySetVertxHandler.PATH) + .handler(new IotDevicePropertySetVertxHandler(deviceDownstreamHandler)); + router.post(IotDevicePropertyGetVertxHandler.PATH) + .handler(new IotDevicePropertyGetVertxHandler(deviceDownstreamHandler)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java new file mode 100644 index 0000000000..5f9906fdc2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IOT 设备服务获取 Vertx Handler + * + * 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class IotDevicePropertyGetVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/service/property/get"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDevicePropertyGetReqDTO reqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + String requestId = body.getString("requestId"); + List identifiers = (List) body.getMap().get("identifiers"); + reqDTO = ((IotDevicePropertyGetReqDTO) new IotDevicePropertyGetReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setIdentifiers(identifiers); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用处理器 + try { + CommonResult result = deviceDownstreamHandler.getDeviceProperty(reqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 属性获取异常]", reqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java new file mode 100644 index 0000000000..c8a60c7708 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IOT 设备服务设置 Vertx Handler + * + * 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class IotDevicePropertySetVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/service/property/set"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDevicePropertySetReqDTO reqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + String requestId = body.getString("requestId"); + Map properties = (Map) body.getMap().get("properties"); + reqDTO = ((IotDevicePropertySetReqDTO) new IotDevicePropertySetReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setProperties(properties); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用处理器 + try { + CommonResult result = deviceDownstreamHandler.setDeviceProperty(reqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 属性设置异常]", reqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java index 22ae7012d8..421fe7484a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java @@ -14,6 +14,11 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; +/** + * IOT 设备服务调用 Vertx Handler + * + * 芋道源码 + */ @Slf4j @RequiredArgsConstructor public class IotDeviceServiceInvokeVertxHandler implements Handler { @@ -26,7 +31,7 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler params = (Map) body.getMap().get("params"); - invokeReqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO() + reqDTO = ((IotDeviceServiceInvokeReqDTO) new IotDeviceServiceInvokeReqDTO() .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) .setIdentifier(identifier).setParams(params); } catch (Exception e) { - log.error("[handle][解析参数失败]", e); + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); return; } - // 2. 调用下游处理器 + // 2. 调用处理器 try { - CommonResult result = deviceDownstreamHandler.invokeDeviceService(invokeReqDTO); + CommonResult result = deviceDownstreamHandler.invokeDeviceService(reqDTO); IotPluginCommonUtils.writeJson(routingContext, result); } catch (Exception e) { - log.error("[handle][请求参数({}) 服务调用异常]", invokeReqDTO, e); + log.error("[handle][请求参数({}) 服务调用异常]", reqDTO, e); IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); } } From 7f0de1e34e102d06e182b8f47af7334ac45cca07 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 31 Jan 2025 18:04:55 +0800 Subject: [PATCH 135/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9Aserver=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E4=B8=8A=E8=A1=8C=E7=9A=84=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=88=E4=B8=8D=E5=8C=85=E6=8B=AC=20http=20=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E9=83=A8=E5=88=86=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/IotDeviceUpstreamServiceImpl.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index ca0263e7e2..6188d0b5d7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -67,7 +67,10 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { } // 2.2 情况二:事件上报 if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) { - // TODO 芋艿:待实现 + reportDeviceEvent(((IotDeviceEventReportReqDTO) + new IotDeviceEventReportReqDTO().setRequestId(requestId).setReportTime(LocalDateTime.now()) + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) + .setIdentifier(simulatorReqVO.getIdentifier()).setParams((Map) simulatorReqVO.getData())); return; } // 2.3 情况三:状态变更 @@ -120,7 +123,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @Override public void reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { // 1.1 获得设备 - log.info("[reportDevicePropertyData][上报设备属性数据: {}]", reportReqDTO); + log.info("[reportDevicePropertyData][上报设备属性: {}]", reportReqDTO); IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); if (device == null) { @@ -141,9 +144,24 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @Override public void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { - log.info("[reportDeviceEventData][上报设备事件数据: {}]", reportReqDTO); + // 1.1 获得设备 + log.info("[reportDeviceEventData][上报设备事件: {}]", reportReqDTO); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); + if (device == null) { + log.error("[reportDeviceEventData][设备({}/{})不存在]", + reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); + return; + } + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, reportReqDTO); - // TODO 芋艿:待实现 + // 2. 发送设备消息 + IotDeviceMessage message = BeanUtils.toBean(reportReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.EVENT.getType()) + .setIdentifier(reportReqDTO.getIdentifier()) + .setData(reportReqDTO.getParams()); + sendDeviceMessage(message, device); } private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { From 2512f2dde8a0df4fd8f3f4d87bfcc65eaaed69cf Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 31 Jan 2025 19:05:42 +0800 Subject: [PATCH 136/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E4=BC=98=E5=8C=96=20http=20?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=20IotDevicePropertyReportVertxHandler=20?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotDevicePropertyGetReqDTO.java | 1 + .../upstream/IotDeviceUpstreamClient.java | 42 ++++----- .../upstream/IotDeviceUpstreamServer.java | 2 +- .../IotDevicePropertyReportVertxHandler.java | 85 +++++++------------ 4 files changed, 51 insertions(+), 79 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java index 061de5745a..b72d88d974 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java @@ -5,6 +5,7 @@ import lombok.Data; import java.util.List; +// TODO @芋艿:从 server => plugin => device 是否有必要?从阿里云 iot 来看,没有这个功能?! /** * IoT 设备【属性】获取 Request DTO * diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java index 8310bad5a9..c71606f62d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java @@ -6,10 +6,11 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEven import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; /** * 设备数据 Upstream 上行客户端 @@ -18,60 +19,51 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; * * @author haohao */ +@RequiredArgsConstructor @Slf4j public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; private final RestTemplate restTemplate; + + // TODO @芋艿:改个名字 private final String deviceDataUrl; - // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 - // TODO @haohao:可以用 lombok 简化 - public IotDeviceUpstreamClient(RestTemplate restTemplate, String deviceDataUrl) { - this.restTemplate = restTemplate; - this.deviceDataUrl = deviceDataUrl; - } - - // TODO @haohao:返回结果,不用 CommonResult 哈。 @Override public CommonResult updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) { String url = deviceDataUrl + URL_PREFIX + "/update-state"; - return doPost(url, updateReqDTO, "updateDeviceState"); + return doPost(url, updateReqDTO); } @Override public CommonResult reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { String url = deviceDataUrl + URL_PREFIX + "/report-event"; - return doPost(url, reportReqDTO, "reportDeviceEventData"); + return doPost(url, reportReqDTO); } @Override public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { String url = deviceDataUrl + URL_PREFIX + "/report-property"; - return doPost(url, reportReqDTO, "reportDevicePropertyData"); + return doPost(url, reportReqDTO); } @Override public CommonResult heartbeatPluginInstance(IotPluginInstanceHeartbeatReqDTO heartbeatReqDTO) { String url = deviceDataUrl + URL_PREFIX + "/heartbeat-plugin-instance"; - return doPost(url, heartbeatReqDTO, "heartbeatPluginInstance"); + return doPost(url, heartbeatReqDTO); } - // TODO @haohao:未来可能有 get 类型哈 - /** - * 将与远程服务交互的通用逻辑抽取成一个私有方法 - */ - private CommonResult doPost(String url, T requestBody, String actionName) { - log.info("[{}] Sending request to URL: {}", actionName, url); + @SuppressWarnings("unchecked") + private CommonResult doPost(String url, T requestBody) { try { - // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 - restTemplate.postForObject(url, requestBody, CommonResult.class); - // TODO @haohao:check 结果,是否成功 - return success(true); + CommonResult result = restTemplate.postForObject(url, requestBody, + (Class>) (Class) CommonResult.class); + log.info("[doPost][url({}) requestBody({}) result({})]", url, requestBody, result); + return result; } catch (Exception e) { - log.error("[{}] Error sending request to URL: {}", actionName, url, e); - return CommonResult.error(400, "Request error: " + e.getMessage()); + log.error("[doPost][url({}) requestBody({}) 发生异常]", url, requestBody, e); + return CommonResult.error(INTERNAL_SERVER_ERROR); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java index e6f4dbb270..87e6a6f85b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java @@ -31,7 +31,7 @@ public class IotDeviceUpstreamServer { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); // 处理 Body router.post(IotDevicePropertyReportVertxHandler.PATH) - .handler(new IotDevicePropertyReportVertxHandler(deviceUpstreamApi)); // 处理设备属性上报 + .handler(new IotDevicePropertyReportVertxHandler(deviceUpstreamApi)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java index 6bf600b145..c75a3a0d51 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java @@ -1,15 +1,15 @@ package cn.iocoder.yudao.module.iot.plugin.http.upstream.router; import cn.hutool.core.util.IdUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; -import io.vertx.ext.web.RequestBody; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,8 +17,13 @@ import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.util.Map; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + /** * IoT 设备属性上报的 Vert.x Handler + * + * @author haohao */ @RequiredArgsConstructor @Slf4j @@ -29,69 +34,43 @@ public class IotDevicePropertyReportVertxHandler implements Handler properties = (Map) body.getMap().get("properties"); + reportReqDTO = ((IotDevicePropertyReportReqDTO) + new IotDevicePropertyReportReqDTO().setRequestId(id) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)) + .setProperties(properties); } catch (Exception e) { - log.error("[HttpVertxHandler] 请求数据解析失败", e); - ctx.response().setStatusCode(400) - .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(createResponseJson(400, null, null, - "请求数据不是合法的 JSON 格式: " + e.getMessage(), - "thing.event.property.post", "1.0").toString()); + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); return; } - String id = jsonData.getStr("id"); - try { - // 设备上线 + // 2. 设备上线 deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) + .setProductKey(reportReqDTO.getProductKey()).setDeviceName(reportReqDTO.getDeviceName())) .setState(IotDeviceStateEnum.ONLINE.getState())); - // 属性上报 - deviceUpstreamApi.reportDeviceProperty(((IotDevicePropertyReportReqDTO) - new IotDevicePropertyReportReqDTO().setRequestId(IdUtil.fastSimpleUUID()) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) - .setProperties((Map) requestBody.asJsonObject().getMap().get("properties"))); - - ctx.response() - .setStatusCode(200) - .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(createResponseJson(200, new JSONObject(), id, "success", - "thing.event.property.post", "1.0").toString()); + // 3.1 属性上报 + CommonResult result = deviceUpstreamApi.reportDeviceProperty(reportReqDTO); + // 3.2 返回结果 + IotPluginCommonUtils.writeJson(routingContext, result); } catch (Exception e) { - log.error("[HttpVertxHandler] 上报属性数据失败", e); - ctx.response() - .setStatusCode(500) - .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(createResponseJson(500, new JSONObject(), id, - "The format of result is error!", - "thing.event.property.post", "1.0").toString()); + log.error("[handle][请求参数({}) 属性获取异常]", reportReqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); } } - // TODO @芋艿:抽一个 IotPluginCommonResult 出来?等 mqtt、websocket 出来后,再考虑优化! - private JSONObject createResponseJson(int code, JSONObject data, String id, - String message, String method, String version) { - JSONObject res = new JSONObject(); - res.set("code", code); - res.set("data", data != null ? data : new JSONObject()); - res.set("id", id); - res.set("message", message); - res.set("method", method); - res.set("version", version); - return res; - } - } From a74459e94e10894ca09fe5e025eb7b78694cacf7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 31 Jan 2025 21:16:01 +0800 Subject: [PATCH 137/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=AE=9E=E7=8E=B0=20IotDeviceEve?= =?UTF-8?q?ntReportVertxHandler=20=E4=BA=8B=E4=BB=B6=E4=B8=8A=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/IotDeviceDownstreamService.java | 2 +- .../IotDeviceDownstreamServiceImpl.java | 2 +- .../control/IotDeviceUpstreamService.java | 2 +- .../control/IotDeviceUpstreamServiceImpl.java | 10 +-- .../upstream/IotDeviceUpstreamServer.java | 3 + .../IotDeviceEventReportVertxHandler.java | 75 +++++++++++++++++++ .../IotDevicePropertyReportVertxHandler.java | 6 +- 7 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java index a3079552d0..6a27f84b16 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceS import jakarta.validation.Valid; /** - * 设备下行 Service 接口 + * IoT 设备下行 Service 接口 * * 目的:服务端 -> 插件 -> 设备 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 7f341f57c3..789e78efd5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -35,7 +35,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_DOWNSTREAM_FAILED; /** - * 设备下行 Service 实现类 + * IoT 设备下行 Service 实现类 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index 592fa7d1cd..ef8f0dd248 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceS import jakarta.validation.Valid; /** - * 设备上行 Service 接口 + * IoT 设备上行 Service 接口 * * 目的:设备 -> 插件 -> 服务端 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index 6188d0b5d7..b2aa6222a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -31,7 +31,7 @@ import java.util.Map; import java.util.Objects; /** - * 设备上行 Service 实现类 + * IoT 设备上行 Service 实现类 * * @author 芋道源码 */ @@ -123,11 +123,11 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @Override public void reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { // 1.1 获得设备 - log.info("[reportDevicePropertyData][上报设备属性: {}]", reportReqDTO); + log.info("[reportDeviceProperty][上报设备属性: {}]", reportReqDTO); IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); if (device == null) { - log.error("[reportDevicePropertyData][设备({}/{})不存在]", + log.error("[reportDeviceProperty][设备({}/{})不存在]", reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); return; } @@ -145,11 +145,11 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @Override public void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { // 1.1 获得设备 - log.info("[reportDeviceEventData][上报设备事件: {}]", reportReqDTO); + log.info("[reportDeviceEvent][上报设备事件: {}]", reportReqDTO); IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); if (device == null) { - log.error("[reportDeviceEventData][设备({}/{})不存在]", + log.error("[reportDeviceEvent][设备({}/{})不存在]", reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); return; } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java index 87e6a6f85b..0e4cebfe87 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.upstream; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDeviceEventReportVertxHandler; import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDevicePropertyReportVertxHandler; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; @@ -32,6 +33,8 @@ public class IotDeviceUpstreamServer { router.route().handler(BodyHandler.create()); // 处理 Body router.post(IotDevicePropertyReportVertxHandler.PATH) .handler(new IotDevicePropertyReportVertxHandler(deviceUpstreamApi)); + router.post(IotDeviceEventReportVertxHandler.PATH) + .handler(new IotDeviceEventReportVertxHandler(deviceUpstreamApi)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java new file mode 100644 index 0000000000..4e0a2ef448 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.iot.plugin.http.upstream.router; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IoT 设备设备上报的 Vert.x Handler + */ +@RequiredArgsConstructor +@Slf4j +public class IotDeviceEventReportVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/event/:identifier/post"; + + private final IotDeviceUpstreamApi deviceUpstreamApi; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDeviceEventReportReqDTO reportReqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + String identifier = routingContext.pathParam("identifier"); + JsonObject body = routingContext.body().asJsonObject(); + String id = ObjUtil.defaultIfBlank(body.getString("id"), IdUtil.fastSimpleUUID()); + Map params = (Map) body.getMap().get("params"); + reportReqDTO = ((IotDeviceEventReportReqDTO) + new IotDeviceEventReportReqDTO().setRequestId(id) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)) + .setIdentifier(identifier).setParams(params); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + try { + // 2. 设备上线 + deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) + new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(reportReqDTO.getProductKey()).setDeviceName(reportReqDTO.getDeviceName())) + .setState(IotDeviceStateEnum.ONLINE.getState())); + + // 3.1 属性上报 + CommonResult result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); + // 3.2 返回结果 + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 时间上报异常]", reportReqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java index c75a3a0d51..c22a6fe996 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java @@ -55,6 +55,10 @@ public class IotDevicePropertyReportVertxHandler implements Handler Date: Fri, 31 Jan 2025 21:51:06 +0800 Subject: [PATCH 138/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E4=BC=98=E5=8C=96=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=20common=20=E5=92=8C=20http=20=E7=9A=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 23 +++++---- .../yudao-spring-boot-starter-mybatis/pom.xml | 4 -- yudao-module-iot/yudao-module-iot-api/pom.xml | 7 --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 15 +++--- .../mapper/plugininfo/PluginInfoMapper.xml | 12 ----- .../plugininstance/PluginInstanceMapper.xml | 12 ----- .../yudao-module-iot-plugin-common/pom.xml | 7 +++ .../IotPluginCommonAutoConfiguration.java | 40 +++++----------- .../config/IotPluginCommonProperties.java | 48 +++++++++++++++++++ .../downstream/IotDeviceDownstreamServer.java | 8 +++- .../IotPluginInstanceHeartbeatJob.java | 12 +++-- .../upstream/IotDeviceUpstreamClient.java | 14 +++--- .../IotPluginHttpAutoConfiguration.java | 15 ++---- .../http/config/IotPluginHttpProperties.java | 17 +++++++ .../upstream/IotDeviceUpstreamServer.java | 9 ++-- .../src/main/resources/application.yml | 16 +++---- 16 files changed, 142 insertions(+), 117 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java rename yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/{heartbeta => heartbeat}/IotPluginInstanceHeartbeatJob.java (84%) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpProperties.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index e7035871fb..05d9aa2b96 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -600,39 +600,42 @@ - - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 - ${mqtt.version} - - org.pf4j pf4j-spring ${pf4j-spring.version} + + + org.slf4j + slf4j-log4j12 + + - + io.vertx vertx-core ${vertx.version} - io.vertx vertx-web ${vertx.version} - io.vertx vertx-mqtt ${vertx.version} + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + ${mqtt.version} + diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 2a14c88b87..1f3c9144bf 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -63,10 +63,6 @@ opengauss-jdbc true - - com.taosdata.jdbc - taos-jdbcdriver - com.alibaba diff --git a/yudao-module-iot/yudao-module-iot-api/pom.xml b/yudao-module-iot/yudao-module-iot-api/pom.xml index ab492729c5..4a31c9bf55 100644 --- a/yudao-module-iot/yudao-module-iot-api/pom.xml +++ b/yudao-module-iot/yudao-module-iot-api/pom.xml @@ -40,13 +40,6 @@ org.pf4j pf4j-spring - - - - org.slf4j - slf4j-log4j12 - - diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 1ac3f915a0..cc3141939d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -52,6 +52,11 @@ yudao-spring-boot-starter-redis + + com.taosdata.jdbc + taos-jdbcdriver + + cn.iocoder.boot @@ -64,12 +69,13 @@ yudao-spring-boot-starter-excel - + io.vertx vertx-web + org.eclipse.paho org.eclipse.paho.client.mqttv3 @@ -78,13 +84,6 @@ org.pf4j pf4j-spring - - - - org.slf4j - slf4j-log4j12 - - diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml deleted file mode 100644 index f24f7e14ce..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml deleted file mode 100644 index df38e85eb6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml index ccf010f24e..1e5a69bfa7 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml @@ -40,6 +40,13 @@ io.vertx vertx-web + + + + org.springframework.boot + spring-boot-starter-validation + true + diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java index 0e5c73b092..7e3d669f20 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java @@ -3,59 +3,43 @@ package cn.iocoder.yudao.module.iot.plugin.common.config; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; -import cn.iocoder.yudao.module.iot.plugin.common.heartbeta.IotPluginInstanceHeartbeatJob; +import cn.iocoder.yudao.module.iot.plugin.common.heartbeat.IotPluginInstanceHeartbeatJob; import cn.iocoder.yudao.module.iot.plugin.common.upstream.IotDeviceUpstreamClient; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.client.RestTemplate; -import java.time.Duration; - /** * IoT 插件的通用自动配置类 * * @author haohao */ @AutoConfiguration +@EnableConfigurationProperties(IotPluginCommonProperties.class) @EnableScheduling // 开启定时任务,因为 IotPluginInstanceHeartbeatJob 是一个定时任务 public class IotPluginCommonAutoConfiguration { - // TODO @haohao:这个要不搞个配置类哈 - @Value("${iot.device-data.url}") - private String deviceDataUrl; - - /** - * 创建 RestTemplate 实例 - * - * @return RestTemplate 实例 - */ @Bean - public RestTemplate restTemplate() { - // 如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 + public RestTemplate restTemplate(IotPluginCommonProperties properties) { return new RestTemplateBuilder() - .connectTimeout(Duration.ofMillis(5000)) // 设置连接超时时间 - .readTimeout(Duration.ofMillis(5000)) // 设置读取超时时间 + .connectTimeout(properties.getUpstreamConnectTimeout()) + .readTimeout(properties.getUpstreamReadTimeout()) .build(); } - /** - * 创建 DeviceDataApi 实例 - * - * @param restTemplate RestTemplate 实例 - * @return DeviceDataApi 实例 - */ @Bean - public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) { - return new IotDeviceUpstreamClient(restTemplate, deviceDataUrl); + public IotDeviceUpstreamApi deviceUpstreamApi(IotPluginCommonProperties properties, + RestTemplate restTemplate) { + return new IotDeviceUpstreamClient(properties, restTemplate); } @Bean(initMethod = "start", destroyMethod = "stop") - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") - public IotDeviceDownstreamServer deviceDownstreamServer(IotDeviceDownstreamHandler deviceDownstreamHandler) { - return new IotDeviceDownstreamServer(deviceDownstreamHandler); + public IotDeviceDownstreamServer deviceDownstreamServer(IotPluginCommonProperties properties, + IotDeviceDownstreamHandler deviceDownstreamHandler) { + return new IotDeviceDownstreamServer(properties, deviceDownstreamHandler); } @Bean(initMethod = "init", destroyMethod = "stop") diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java new file mode 100644 index 0000000000..556786507c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.iot.plugin.common.config; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import java.time.Duration; + +@ConfigurationProperties(prefix = "yudao.iot.plugin.common") +@Validated +@Data +public class IotPluginCommonProperties { + + /** + * 上行连接超时的默认值 + */ + public static final Duration UPSTREAM_CONNECT_TIMEOUT_DEFAULT = Duration.ofSeconds(30); + /** + * 上行读取超时的默认值 + */ + public static final Duration UPSTREAM_READ_TIMEOUT_DEFAULT = Duration.ofSeconds(30); + + /** + * 下行端口 - 随机 + */ + public static final Integer DOWNSTREAM_PORT_RANDOM = 0; + + /** + * 上行 URL + */ + @NotEmpty(message = "上行 URL 不能为空") + private String upstreamUrl; + /** + * 上行连接超时 + */ + private Duration upstreamConnectTimeout = UPSTREAM_CONNECT_TIMEOUT_DEFAULT; + /** + * 上行读取超时 + */ + private Duration upstreamReadTimeout = UPSTREAM_READ_TIMEOUT_DEFAULT; + + /** + * 下行端口 + */ + private Integer downstreamPort = DOWNSTREAM_PORT_RANDOM; + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java index 4c45e972e5..797d36d45b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertyGetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertySetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceServiceInvokeVertxHandler; @@ -19,8 +20,11 @@ public class IotDeviceDownstreamServer { private final Vertx vertx; private final HttpServer server; + private final IotPluginCommonProperties properties; - public IotDeviceDownstreamServer(IotDeviceDownstreamHandler deviceDownstreamHandler) { + public IotDeviceDownstreamServer(IotPluginCommonProperties properties, + IotDeviceDownstreamHandler deviceDownstreamHandler) { + this.properties = properties; // 创建 Vertx 实例 this.vertx = Vertx.vertx(); // 创建 Router 实例 @@ -41,7 +45,7 @@ public class IotDeviceDownstreamServer { */ public void start() { log.info("[start][开始启动]"); - server.listen(0) // 通过 0 自动选择端口 + server.listen(properties.getDownstreamPort()) .toCompletionStage() .toCompletableFuture() .join(); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeta/IotPluginInstanceHeartbeatJob.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java similarity index 84% rename from yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeta/IotPluginInstanceHeartbeatJob.java rename to yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java index fe0244bc94..238d34f98a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeta/IotPluginInstanceHeartbeatJob.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.plugin.common.heartbeta; +package cn.iocoder.yudao.module.iot.plugin.common.heartbeat; import cn.hutool.system.SystemUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInst import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import java.util.concurrent.TimeUnit; @@ -17,6 +18,7 @@ import java.util.concurrent.TimeUnit; * 用于定时发送心跳给服务端 */ @RequiredArgsConstructor +@Slf4j public class IotPluginInstanceHeartbeatJob { private final IotDeviceUpstreamApi deviceUpstreamApi; @@ -24,22 +26,22 @@ public class IotPluginInstanceHeartbeatJob { public void init() { CommonResult result = deviceUpstreamApi.heartbeatPluginInstance(buildPluginInstanceHeartbeatReqDTO(true)); - // TODO @芋艿:结果的处理 + log.info("[init][上线结果:{})]", result); } public void stop() { CommonResult result = deviceUpstreamApi.heartbeatPluginInstance(buildPluginInstanceHeartbeatReqDTO(false)); - // TODO @芋艿:结果的处理 + log.info("[stop][下线结果:{})]", result); } @Scheduled(initialDelay = 3, fixedRate = 3, timeUnit = TimeUnit.MINUTES) // 3 分钟执行一次 public void execute() { CommonResult result = deviceUpstreamApi.heartbeatPluginInstance(buildPluginInstanceHeartbeatReqDTO(true)); - // TODO @芋艿:结果的处理 + log.info("[execute][心跳结果:{})]", result); } private IotPluginInstanceHeartbeatReqDTO buildPluginInstanceHeartbeatReqDTO(Boolean online) { - // TODO @芋艿:pluginKey 的获取??? + // TODO @haohao:pluginKey 的获取??? return new IotPluginInstanceHeartbeatReqDTO() .setPluginKey("yudao-module-iot-plugin-http").setProcessId(IotPluginCommonUtils.getProcessId()) .setHostIp(SystemUtil.getHostInfo().getAddress()).setDownstreamPort(deviceDownstreamServer.getPort()) diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java index c71606f62d..f3934cb0da 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEven import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate; @@ -25,32 +26,31 @@ public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; - private final RestTemplate restTemplate; + private final IotPluginCommonProperties properties; - // TODO @芋艿:改个名字 - private final String deviceDataUrl; + private final RestTemplate restTemplate; @Override public CommonResult updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/update-state"; + String url = properties.getUpstreamUrl() + URL_PREFIX + "/update-state"; return doPost(url, updateReqDTO); } @Override public CommonResult reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/report-event"; + String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-event"; return doPost(url, reportReqDTO); } @Override public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/report-property"; + String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property"; return doPost(url, reportReqDTO); } @Override public CommonResult heartbeatPluginInstance(IotPluginInstanceHeartbeatReqDTO heartbeatReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/heartbeat-plugin-instance"; + String url = properties.getUpstreamUrl() + URL_PREFIX + "/heartbeat-plugin-instance"; return doPost(url, heartbeatReqDTO); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java index 4e402f91fd..63e55f58fe 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpAutoConfiguration.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; import cn.iocoder.yudao.module.iot.plugin.http.downstream.IotDeviceDownstreamHandlerImpl; import cn.iocoder.yudao.module.iot.plugin.http.upstream.IotDeviceUpstreamServer; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,18 +14,13 @@ import org.springframework.context.annotation.Configuration; * @author haohao */ @Configuration +@EnableConfigurationProperties(IotPluginHttpProperties.class) public class IotPluginHttpAutoConfiguration { - // TODO @haohao:这个要不要搞个配置类,更容易维护; - /** - * 可在 application.yml 中配置,默认端口 8092 - */ - @Value("${plugin.http.server.port:8092}") - private Integer port; - @Bean(initMethod = "start", destroyMethod = "stop") - public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi) { - return new IotDeviceUpstreamServer(port, deviceUpstreamApi); + public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, + IotPluginHttpProperties properties) { + return new IotDeviceUpstreamServer(properties, deviceUpstreamApi); } @Bean diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpProperties.java new file mode 100644 index 0000000000..49dca81261 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotPluginHttpProperties.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.iot.plugin.http.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +@ConfigurationProperties(prefix = "yudao.iot.plugin.http") +@Validated +@Data +public class IotPluginHttpProperties { + + /** + * HTTP 服务端口 + */ + private Integer serverPort; + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java index 0e4cebfe87..42da951a24 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.upstream; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.plugin.http.config.IotPluginHttpProperties; import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDeviceEventReportVertxHandler; import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDevicePropertyReportVertxHandler; import io.vertx.core.Vertx; @@ -21,11 +22,11 @@ public class IotDeviceUpstreamServer { private final Vertx vertx; private final HttpServer server; - private final Integer port; + private final IotPluginHttpProperties properties; - public IotDeviceUpstreamServer(Integer port, + public IotDeviceUpstreamServer(IotPluginHttpProperties properties, IotDeviceUpstreamApi deviceUpstreamApi) { - this.port = port; + this.properties = properties; // 创建 Vertx 实例 this.vertx = Vertx.vertx(); // 创建 Router 实例 @@ -44,7 +45,7 @@ public class IotDeviceUpstreamServer { */ public void start() { log.info("[start][开始启动]"); - server.listen(port) + server.listen(properties.getServerPort()) .toCompletionStage() .toCompletableFuture() .join(); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml index f7f89e3e67..4afeb4f26f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml @@ -2,11 +2,11 @@ spring: application: name: yudao-module-iot-plugin-http -iot: - device-data: - url: http://127.0.0.1:48080 - -plugin: - http: - server: - port: 8092 \ No newline at end of file +yudao: + iot: + plugin: + common: + upstream-url: http://127.0.0.1:48080 + downstream-port: 8093 + http: + server-port: 8092 From 47c281d933ffe3ace90ba73e66dcfc1863b33fb8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 31 Jan 2025 22:47:04 +0800 Subject: [PATCH 139/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=8E=BB=E9=99=A4=20Simulation?= =?UTF-8?q?=20=E5=85=B3=E9=94=AE=E5=AD=97=EF=BC=8C=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E6=9B=B4=E6=B8=85=E6=99=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/device/IotDeviceController.java | 26 +++++++++---------- ...qVO.java => IotDeviceDownstreamReqVO.java} | 4 +-- ...ReqVO.java => IotDeviceUpstreamReqVO.java} | 4 +-- .../dal/dataobject/device/IotDeviceDO.java | 7 +++++ .../control/IotDeviceDownstreamService.java | 6 ++--- .../IotDeviceDownstreamServiceImpl.java | 10 +++---- .../control/IotDeviceUpstreamService.java | 6 ++--- .../control/IotDeviceUpstreamServiceImpl.java | 4 +-- 8 files changed, 36 insertions(+), 31 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/{IotDeviceSimulationDownstreamReqVO.java => IotDeviceDownstreamReqVO.java} (87%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/{IotDeviceSimulationUpstreamReqVO.java => IotDeviceUpstreamReqVO.java} (87%) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 6d73e0a456..24d4263816 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -6,9 +6,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; @@ -159,21 +159,19 @@ public class IotDeviceController { ExcelUtils.write(response, "设备导入模板.xls", "数据", IotDeviceImportExcelVO.class, list); } - @PostMapping("/simulation-upstream") - @Operation(summary = "模拟设备上行") - @PreAuthorize("@ss.hasPermission('iot:device:simulation')") - public CommonResult simulationDeviceUpstream( - @Valid @RequestBody IotDeviceSimulationUpstreamReqVO upstreamReqVO) { - deviceUpstreamService.simulationDeviceUpstream(upstreamReqVO); + @PostMapping("/upstream") + @Operation(summary = "设备上行", description = "可用于设备模拟") + @PreAuthorize("@ss.hasPermission('iot:device:upstream')") + public CommonResult upstreamDevice(@Valid @RequestBody IotDeviceUpstreamReqVO upstreamReqVO) { + deviceUpstreamService.upstreamDevice(upstreamReqVO); return success(true); } - @PostMapping("/simulation-downstream") - @Operation(summary = "模拟设备下行") - @PreAuthorize("@ss.hasPermission('iot:device:simulation')") - public CommonResult simulationDownstreamDevice( - @Valid @RequestBody IotDeviceSimulationDownstreamReqVO downstreamReqVO) { - deviceDownstreamService.simulationDeviceDownstream(downstreamReqVO); + @PostMapping("/downstream") + @Operation(summary = "设备下行", description = "可用于设备模拟") + @PreAuthorize("@ss.hasPermission('iot:device:downstream')") + public CommonResult downstreamDevice(@Valid @RequestBody IotDeviceDownstreamReqVO downstreamReqVO) { + deviceDownstreamService.downstreamDevice(downstreamReqVO); return success(true); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceSimulationDownstreamReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java similarity index 87% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceSimulationDownstreamReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java index 3d8c855513..eefaeffebc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceSimulationDownstreamReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceDownstreamReqVO.java @@ -7,9 +7,9 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; -@Schema(description = "管理后台 - IoT 模拟设备下行 Request VO") // 服务调用、属性设置、属性获取等 +@Schema(description = "管理后台 - IoT 设备下行 Request VO") // 服务调用、属性设置、属性获取等 @Data -public class IotDeviceSimulationDownstreamReqVO { +public class IotDeviceDownstreamReqVO { @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") @NotNull(message = "设备编号不能为空") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceSimulationUpstreamReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java similarity index 87% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceSimulationUpstreamReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java index 43fc5bfa2a..778d75bba8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceSimulationUpstreamReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/control/IotDeviceUpstreamReqVO.java @@ -7,9 +7,9 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; -@Schema(description = "管理后台 - IoT 模拟设备上行 Request VO") // 属性上报、事件上报、状态变更等 +@Schema(description = "管理后台 - IoT 设备上行 Request VO") // 属性上报、事件上报、状态变更等 @Data -public class IotDeviceSimulationUpstreamReqVO { +public class IotDeviceUpstreamReqVO { @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") @NotNull(message = "设备编号不能为空") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index defb0c2f0a..c146f8248d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -160,4 +160,11 @@ public class IotDeviceDO extends BaseDO { */ private String address; + /** + * 设备配置 + * + * JSON 格式,可下发给 device 进行自定义配置 + */ + private String config; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java index 6a27f84b16..433b15320b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.service.device.control; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; import jakarta.validation.Valid; /** @@ -13,10 +13,10 @@ import jakarta.validation.Valid; public interface IotDeviceDownstreamService { /** - * 模拟设备下行 + * 设备下行,可用于设备模拟 * * @param downstreamReqVO 设备下行请求 VO */ - void simulationDeviceDownstream(@Valid IotDeviceSimulationDownstreamReqVO downstreamReqVO); + void downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 789e78efd5..4225e7b4c3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceDo import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationDownstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; @@ -56,7 +56,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic private IotDeviceProducer deviceProducer; @Override - public void simulationDeviceDownstream(IotDeviceSimulationDownstreamReqVO downstreamReqVO) { + public void downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) { // 校验设备是否存在 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); // TODO 芋艿:父设备的处理 @@ -93,7 +93,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param parentDevice 父设备 */ @SuppressWarnings("unchecked") - private void invokeDeviceService(IotDeviceSimulationDownstreamReqVO downstreamReqVO, + private void invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO, IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { @@ -130,7 +130,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param parentDevice 父设备 */ @SuppressWarnings("unchecked") - private void setDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, + private void setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { @@ -168,7 +168,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param parentDevice 父设备 */ @SuppressWarnings("unchecked") - private void getDeviceProperty(IotDeviceSimulationDownstreamReqVO downstreamReqVO, + private void getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof List)) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index ef8f0dd248..8bc0ffd697 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device.control; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import jakarta.validation.Valid; /** @@ -16,11 +16,11 @@ import jakarta.validation.Valid; public interface IotDeviceUpstreamService { /** - * 模拟设备上行 + * 设备上行,可用于设备模拟 * * @param simulatorReqVO 设备上行请求 VO */ - void simulationDeviceUpstream(@Valid IotDeviceSimulationUpstreamReqVO simulatorReqVO); + void upstreamDevice(@Valid IotDeviceUpstreamReqVO simulatorReqVO); /** * 更新设备状态 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index b2aa6222a3..9a1276ef4b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEven import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceUpstreamAbstractReqDTO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; @@ -52,7 +52,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @Override @SuppressWarnings("unchecked") - public void simulationDeviceUpstream(IotDeviceSimulationUpstreamReqVO simulatorReqVO) { + public void upstreamDevice(IotDeviceUpstreamReqVO simulatorReqVO) { // 1. 校验存在 IotDeviceDO device = deviceService.validateDeviceExists(simulatorReqVO.getId()); From f46a2fb011282f33b6e61224a1334e6bc04151e1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 31 Jan 2025 23:14:09 +0800 Subject: [PATCH 140/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20device=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=8B=E5=8F=91=EF=BC=88=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=EF=BC=89=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../downstream/IotDeviceConfigSetReqDTO.java | 22 +++++++ .../IotDeviceMessageIdentifierEnum.java | 8 ++- .../device/IotDeviceMessageTypeEnum.java | 3 +- .../admin/device/IotDeviceController.http | 24 ++++++-- .../IotDeviceDownstreamServiceImpl.java | 59 +++++++++++++++--- .../IotDeviceDownstreamHandler.java | 9 +++ .../downstream/IotDeviceDownstreamServer.java | 3 + .../IotDeviceConfigSetVertxHandler.java | 61 +++++++++++++++++++ .../IotDeviceDownstreamHandlerImpl.java | 6 ++ 9 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java new file mode 100644 index 0000000000..9624b671ee --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Map; + +/** + * IoT 设备【配置】设置 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDeviceConfigSetReqDTO extends IotDeviceDownstreamAbstractReqDTO { + + /** + * 配置 + */ + @NotNull(message = "配置不能为空") + private Map config; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index 03bd7abec2..c1a31d59b9 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -10,19 +10,21 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public enum IotDeviceMessageIdentifierEnum { - PROPERTY_GET("get"), // 下行 + PROPERTY_GET("get"), // 下行 TODO 芋艿:【讨论】貌似这个“上行”更合理?device 主动拉取配置。和 IotDevicePropertyGetReqDTO 一样的配置 PROPERTY_SET("set"), // 下行 PROPERTY_REPORT("report"), // 上行 STATE_ONLINE("online"), // 上行 STATE_OFFLINE("offline"), // 上行 - SERVICE_REPLY_SUFFIX("_reply"); // TODO 上行 or 下行 + CONFIG_GET("get"), // 上行 TODO 芋艿:【讨论】暂时没有上行的场景 + CONFIG_SET("set"), // 下行 + SERVICE_REPLY_SUFFIX("_reply"); // 芋艿:TODO 芋艿:【讨论】上行 or 下行 /** * 标志符 */ private final String identifier; -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java index 473b542ecd..45b7596ced 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -16,7 +16,8 @@ public enum IotDeviceMessageTypeEnum implements ArrayValuable { STATE("state"), // 设备状态 PROPERTY("property"), // 设备属性 EVENT("event"), // 设备事件 - SERVICE("service"); // 设备服务 + SERVICE("service"), // 设备服务 + CONFIG("config"); // 设备配置 public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http index 34df37e93b..042ad7e7f2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http @@ -1,5 +1,5 @@ -### 请求 /iot/device/simulation-downstream 接口(服务调用) => 成功 -POST {{baseUrl}}/iot/device/simulation-downstream +### 请求 /iot/device/downstream 接口(服务调用) => 成功 +POST {{baseUrl}}/iot/device/downstream Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -13,8 +13,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/device/simulation-downstream 接口(属性设置) => 成功 -POST {{baseUrl}}/iot/device/simulation-downstream +### 请求 /iot/device/downstream 接口(属性设置) => 成功 +POST {{baseUrl}}/iot/device/downstream Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -28,8 +28,8 @@ Authorization: Bearer {{token}} } } -### 请求 /iot/device/simulation-downstream 接口(属性获取) => 成功 -POST {{baseUrl}}/iot/device/simulation-downstream +### 请求 /iot/device/downstream 接口(属性获取) => 成功 +POST {{baseUrl}}/iot/device/downstream Content-Type: application/json tenant-id: {{adminTenentId}} Authorization: Bearer {{token}} @@ -39,4 +39,16 @@ Authorization: Bearer {{token}} "type": "property", "identifier": "get", "data": ["xx", "yy"] +} + +### 请求 /iot/device/downstream 接口(配置设置) => 成功 +POST {{baseUrl}}/iot/device/downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "config", + "identifier": "set" } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 4225e7b4c3..6db8a76ab3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -5,10 +5,8 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceDownstreamAbstractReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; @@ -68,21 +66,27 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic return; } // 属性相关 - if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) + if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { // 属性设置 if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { setDeviceProperty(downstreamReqVO, device, parentDevice); return; } - // 属性设置 - if (Objects.equals(downstreamReqVO.getIdentifier(), - IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { - getDeviceProperty(downstreamReqVO, device, parentDevice); + // 属性设置 + if (Objects.equals(downstreamReqVO.getIdentifier(), + IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { + getDeviceProperty(downstreamReqVO, device, parentDevice); + return; + } + } + // 配置下发 + if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType()) + && Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) { + setDeviceConfig(downstreamReqVO, device, parentDevice); return; } // TODO 芋艿:ota 升级 - // TODO 芋艿:配置下发 } /** @@ -198,6 +202,41 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic } } + /** + * 设置设备配置 + * + * @param downstreamReqVO 下行请求 + * @param device 设备 + * @param parentDevice 父设备 + */ + @SuppressWarnings({"unchecked", "unused"}) + private void setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数转换,无需校验 + Map config = JsonUtils.parseObject(device.getConfig(), Map.class); + + // 2. 发送请求 + String url = String.format( "sys/%s/%s/thing/service/config/set", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); + IotDeviceConfigSetReqDTO reqDTO = new IotDeviceConfigSetReqDTO() + .setConfig(config); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.CONFIG.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier()) + .setData(reqDTO.getConfig()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[setDeviceConfig][设备({})配置下发失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } + } + /** * 请求插件 * diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java index 127332cc7f..62d72785d9 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceConfigSetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; @@ -38,4 +39,12 @@ public interface IotDeviceDownstreamHandler { */ CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO); + /** + * 设置设备配置 + * + * @param setReqDTO 设置设备配置的请求 + * @return 是否成功 + */ + CommonResult setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java index 797d36d45b..6b08dba009 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceConfigSetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertyGetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertySetVertxHandler; import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceServiceInvokeVertxHandler; @@ -36,6 +37,8 @@ public class IotDeviceDownstreamServer { .handler(new IotDevicePropertySetVertxHandler(deviceDownstreamHandler)); router.post(IotDevicePropertyGetVertxHandler.PATH) .handler(new IotDevicePropertyGetVertxHandler(deviceDownstreamHandler)); + router.post(IotDeviceConfigSetVertxHandler.PATH) + .handler(new IotDeviceConfigSetVertxHandler(deviceDownstreamHandler)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java new file mode 100644 index 0000000000..337db248f6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceConfigSetReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IOT 设备配置设置 Vertx Handler + * + * 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class IotDeviceConfigSetVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/service/config/set"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDeviceConfigSetReqDTO reqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + String requestId = body.getString("requestId"); + Map config = (Map) body.getMap().get("config"); + reqDTO = ((IotDeviceConfigSetReqDTO) new IotDeviceConfigSetReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setConfig(config); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用处理器 + try { + CommonResult result = deviceDownstreamHandler.setDeviceConfig(reqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) 配置设置异常]", reqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java index b70052530c..3c86ad6d26 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.downstream; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceConfigSetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; @@ -33,4 +34,9 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); } + @Override + public CommonResult setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); + } + } From 06749a18fcd8a1b5e81a7b0352eb1c5b4efef2b4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Feb 2025 20:35:41 +0800 Subject: [PATCH 141/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=E8=A7=84?= =?UTF-8?q?=E5=88=99=E5=BC=95=E6=93=8E=E7=9A=84=20IotRuleSceneDO=20?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E8=81=94=E5=8A=A8=E7=9A=84=E5=AE=9E=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotDeviceMessageIdentifierEnum.java | 1 + .../dal/dataobject/rule/IotRuleSceneDO.java | 236 ++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index c1a31d59b9..8f6270c7b9 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -20,6 +20,7 @@ public enum IotDeviceMessageIdentifierEnum { CONFIG_GET("get"), // 上行 TODO 芋艿:【讨论】暂时没有上行的场景 CONFIG_SET("set"), // 下行 + SERVICE_INVOKE("${identifier}"), // 下行 SERVICE_REPLY_SUFFIX("_reply"); // 芋艿:TODO 芋艿:【讨论】上行 or 下行 /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java new file mode 100644 index 0000000000..91b62bca60 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java @@ -0,0 +1,236 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +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 lombok.*; + +import java.util.List; +import java.util.Map; + +/** + * IoT 场景联动 DO + * + * @author 芋道源码 + */ +@TableName("iot_rule_scene") +@KeySequence("iot_rule_scene_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotRuleSceneDO extends BaseDO { + + /** + * 场景编号 + */ + @TableId + private Long id; + /** + * 场景名称 + */ + private String name; + /** + * 场景描述 + */ + private String description; + /** + * 场景状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + + /** + * 触发器数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List triggers; + + /** + * 执行器数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List actuators; + + /** + * 触发器 + */ + @Data + public static class Trigger { + + /** + * 触发类型 + * + * TODO @芋艿:device、job + */ + private Integer type; + + /** + * 产品标识 + * + * 关联 {@link IotProductDO#getProductKey()} + */ + private String productKey; + /** + * 设备名称数组 + * + * 关联 {@link IotDeviceDO#getDeviceName()} + */ + private List deviceNames; + + /** + * 触发条件数组 + * + * TODO @芋艿:注释说明 + */ + private List conditions; + + /** + * CRON 表达式 + * + * TODO @芋艿:注释说明 + */ + private String cronExpression; + + } + + /** + * 触发条件 + */ + @Data + public static class TriggerCondition { + + /** + * 消息类型 + * + * 枚举 {@link IotDeviceMessageTypeEnum} + */ + private String type; + /** + * 消息标识符 + * + * 枚举 {@link IotDeviceMessageIdentifierEnum} + */ + private String identifier; + + /** + * 参数数组 + */ + private List parameters; + + } + + /** + * 触发条件参数 + */ + @Data + public static class TriggerConditionParameter { + + /** + * 标识符(属性、事件、服务) + * + * 关联 {@link IotThingModelDO#getIdentifier()} + */ + private String identifier; + + /** + * 操作符 + * + * TODO 芋艿:枚举 + */ + private String operator; + + /** + * 值 + */ + private String value; + + } + + /** + * 执行器 + */ + @Data + public static class Actuator { + + /** + * 执行类型 + * + * TODO @芋艿:control、alert、webhook(待定) + */ + private Integer type; + + /** + * 产品标识 + * + * 关联 {@link IotProductDO#getProductKey()} + */ + private String productKey; + /** + * 设备名称数组 + * + * 关联 {@link IotDeviceDO#getDeviceName()} + */ + private List deviceNames; + + /** + * 控制数组 + * + * TODO 芋艿:类型的情况下 + */ + private List controls; + + /** + * 数据桥接编号 + * + * TODO 芋艿:暂定! + * TODO 芋艿:关联 + */ + private Long bridgeId; + + } + + /** + * 执行器控制 + */ + @Data + public static class ActuatorControl { + + /** + * 消息类型 + * + * 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE} + */ + private Integer type; + /** + * 消息标识符 + * + * 枚举 {@link IotDeviceMessageIdentifierEnum} + * + * 1. 属性设置:对应 {@link IotDeviceMessageIdentifierEnum#PROPERTY_SET} + * 2. 服务调用:对应 {@link IotDeviceMessageIdentifierEnum#SERVICE_INVOKE} + */ + private String identifier; + + /** + * 具体数据 + * + * 1. 属性设置:在 {@link #type} 是 {@link IotDeviceMessageTypeEnum#PROPERTY} 时,对应 properties + * 2. 服务调用:在 {@link #type} 是 {@link IotDeviceMessageTypeEnum#SERVICE} 时,对应 params + */ + private Map data; + + } + +} From a4be3bb84d0a24169f085a643edbd24eedb35aa5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Feb 2025 19:44:38 +0800 Subject: [PATCH 142/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20IotRuleScene?= =?UTF-8?q?MessageHandler=20=E5=A4=84=E7=90=86=E8=A7=84=E5=88=99=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=EF=BC=8C=E5=B0=9D=E8=AF=95=E5=9F=BA=E4=BA=8E=20Spring?= =?UTF-8?q?=20El=20=E8=A1=A8=E8=BE=BE=E5=BC=8F=E5=AE=9E=E7=8E=B0=E5=88=9D?= =?UTF-8?q?=E6=AD=A5=E8=AE=A1=E7=AE=97=EF=BC=88=E9=83=A8=E5=88=86=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=EF=BC=89=20trigger=20=E6=9D=A1=E4=BB=B6=E5=8C=B9?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...TriggerConditionParameterOperatorEnum.java | 51 +++++ .../rule/IotRuleSceneTriggerTypeEnum.java | 30 +++ .../dal/dataobject/rule/IotRuleSceneDO.java | 12 +- .../dal/mysql/rule/IotRuleSceneMapper.java | 10 + .../rule/IotRuleSceneMessageHandler.java | 30 +++ .../iot/service/rule/IotRuleSceneService.java | 31 +++ .../service/rule/IotRuleSceneServiceImpl.java | 212 ++++++++++++++++++ 7 files changed, 372 insertions(+), 4 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java new file mode 100644 index 0000000000..f1a78cf31b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Iot 场景触发条件参数的操作符枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayValuable { + + EQUALS("=", "%s == %s"), + NOT_EQUALS("!=", "%s != %s"), + + GREATER_THAN(">", "%s > %s"), + GREATER_THAN_OR_EQUALS(">=", "%s >= %s"), + + LESS_THAN("<", "%s < %s"), + LESS_THAN_OR_EQUALS("<=", "%s <= %s"), + + IN("in", "%s in { %s }"), + NOT_IN("not in", "%s not in { %s }"), + + BETWEEN("between", "(%s >= %s) && (%s <= %s)"), + NOT_BETWEEN("not between", "!(%s between %s and %s)"), + + LIKE("like", "%s like %s"), // 字符串匹配 + NOT_NULL("not null", ""); // 非空 + + private final String operator; + private final String springExpression; + + public static final String[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerConditionParameterOperatorEnum::getOperator).toArray(String[]::new); + + public static IotRuleSceneTriggerConditionParameterOperatorEnum operatorOf(String operator) { + return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values()); + } + + @Override + public String[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java new file mode 100644 index 0000000000..509b9a6032 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Iot 场景流转的触发类型枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum IotRuleSceneTriggerTypeEnum implements ArrayValuable { + + DEVICE(1), // 设备触发 + TIMER(2); // 定时触发 + + private final Integer type; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java index 91b62bca60..aa6c314253 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -17,7 +18,7 @@ import java.util.List; import java.util.Map; /** - * IoT 场景联动 DO + * IoT 规则场景(场景联动) DO * * @author 芋道源码 */ @@ -72,7 +73,7 @@ public class IotRuleSceneDO extends BaseDO { /** * 触发类型 * - * TODO @芋艿:device、job + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum} */ private Integer type; @@ -147,12 +148,15 @@ public class IotRuleSceneDO extends BaseDO { /** * 操作符 * - * TODO 芋艿:枚举 + * 枚举 {@link IotRuleSceneTriggerConditionParameterOperatorEnum} */ private String operator; /** - * 值 + * 比较值 + * + * 如果有多个值,则使用 "," 分隔,类似 "1,2,3"。 + * 例如说,{@link IotRuleSceneTriggerConditionParameterOperatorEnum#IN}、{@link IotRuleSceneTriggerConditionParameterOperatorEnum#BETWEEN} */ private String value; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java new file mode 100644 index 0000000000..e5e069a0cb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotRuleSceneMapper.java @@ -0,0 +1,10 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.rule; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface IotRuleSceneMapper extends BaseMapperX { + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java new file mode 100644 index 0000000000..bdd148ec07 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.mq.consumer.rule; + +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * 针对 {@link IotDeviceMessage} 的消费者,处理规则场景 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class IotRuleSceneMessageHandler { + + @Resource + private IotRuleSceneService ruleSceneService; + + @EventListener + @Async + public void onMessage(IotDeviceMessage message) { + log.info("[onMessage][消息内容({})]", message); + ruleSceneService.executeRuleScene(message); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java new file mode 100644 index 0000000000..d1ebfd4f17 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.iot.service.rule; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; + +import java.util.List; + +/** + * IoT 规则场景 Service 接口 + * + * @author 芋道源码 + */ +public interface IotRuleSceneService { + + /** + * 【缓存】获得指定设备的场景列表 + * + * @param productKey 产品 Key + * @param deviceName 设备名称 + * @return 场景列表 + */ + List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); + + /** + * 执行规则场景 + * + * @param message 消息 + */ + void executeRuleScene(IotDeviceMessage message); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java new file mode 100644 index 0000000000..0d8d0cdddf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -0,0 +1,212 @@ +package cn.iocoder.yudao.module.iot.service.rule; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.text.CharPool; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; + +/** + * IoT 规则场景 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotRuleSceneServiceImpl implements IotRuleSceneService { + + @Resource + private IotRuleSceneMapper ruleSceneMapper; + + // TODO 芋艿,缓存待实现 + @Override + @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略 + public List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { + if (true) { + IotRuleSceneDO ruleScene01 = new IotRuleSceneDO(); + ruleScene01.setTriggers(CollUtil.newArrayList()); + IotRuleSceneDO.Trigger trigger01 = new IotRuleSceneDO.Trigger(); + trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType()); + trigger01.setConditions(CollUtil.newArrayList()); + IotRuleSceneDO.TriggerCondition condition01 = new IotRuleSceneDO.TriggerCondition(); + condition01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); + condition01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()); + condition01.setParameters(CollUtil.newArrayList()); + IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter011.setIdentifier("width"); + parameter011.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); + parameter011.setValue("1"); + condition01.getParameters().add(parameter011); + IotRuleSceneDO.TriggerConditionParameter parameter012 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter012.setIdentifier("width"); + parameter012.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_EQUALS.getOperator()); + parameter012.setValue("2"); + condition01.getParameters().add(parameter012); + IotRuleSceneDO.TriggerConditionParameter parameter013 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter013.setIdentifier("width"); + parameter013.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN.getOperator()); + parameter013.setValue("0"); + condition01.getParameters().add(parameter013); + IotRuleSceneDO.TriggerConditionParameter parameter014 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter014.setIdentifier("width"); + parameter014.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator()); + parameter014.setValue("0"); + condition01.getParameters().add(parameter014); + IotRuleSceneDO.TriggerConditionParameter parameter015 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter015.setIdentifier("width"); + parameter015.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN.getOperator()); + parameter015.setValue("2"); + condition01.getParameters().add(parameter015); + IotRuleSceneDO.TriggerConditionParameter parameter016 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter016.setIdentifier("width"); + parameter016.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS.getOperator()); + parameter016.setValue("2"); + condition01.getParameters().add(parameter016); + IotRuleSceneDO.TriggerConditionParameter parameter017 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter017.setIdentifier("width"); + parameter017.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.IN.getOperator()); + parameter017.setValue("1,2,3"); + condition01.getParameters().add(parameter017); + IotRuleSceneDO.TriggerConditionParameter parameter018 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter018.setIdentifier("width"); + parameter018.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN.getOperator()); + parameter018.setValue("0,2,3"); + condition01.getParameters().add(parameter018); + IotRuleSceneDO.TriggerConditionParameter parameter019 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter019.setIdentifier("width"); + parameter019.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN.getOperator()); + parameter019.setValue("1,3"); + condition01.getParameters().add(parameter019); + IotRuleSceneDO.TriggerConditionParameter parameter020 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter020.setIdentifier("width"); + parameter020.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN.getOperator()); + parameter020.setValue("2,3"); + condition01.getParameters().add(parameter020); + trigger01.getConditions().add(condition01); + ruleScene01.getTriggers().add(trigger01); + + return ListUtil.toList(ruleScene01); + } + + List list = ruleSceneMapper.selectList(); + // TODO @芋艿:需要考虑开启状态 + return filterList(list, ruleScene -> { + for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { + if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) { + continue; + } + if (CollUtil.isEmpty(trigger.getDeviceNames()) // 无设备名称限制 + || trigger.getDeviceNames().contains(deviceName)) { // 包含设备名称 + return true; + } + } + return false; + }); + } + + @Override + public void executeRuleScene(IotDeviceMessage message) { + // 1. 获得设备匹配的规则场景 + List ruleScenes = getMatchedRuleSceneList(message); + if (CollUtil.isEmpty(ruleScenes)) { + return; + } + } + + /** + * 获得匹配的规则场景列表 + * + * @param message 设备消息 + * @return 规则场景列表 + */ + @SuppressWarnings("unchecked") + private List getMatchedRuleSceneList(IotDeviceMessage message) { + // 1. 匹配设备 + // TODO @芋艿:可能需要 getSelf(); 缓存 + List ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache( + message.getProductKey(), message.getDeviceName()); + if (CollUtil.isEmpty(ruleScenes)) { + return ruleScenes; + } + + // 2. 匹配 trigger 触发器的条件 + return filterList(ruleScenes, ruleScene -> { + for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { + // 非设备触发,不匹配 + if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) { + return false; + } + // TODO 芋艿:产品、设备的匹配,要不要这里在做一次???貌似和 1. 部分重复了 + // 条件为空,说明没有匹配的条件,因此不匹配 + if (CollUtil.isEmpty(trigger.getConditions())) { + return false; + } + IotRuleSceneDO.TriggerCondition found = CollUtil.findOne(trigger.getConditions(), condition -> { + if (ObjUtil.notEqual(message.getType(), condition.getType()) + || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) { + return false; + } + // TODO @芋艿:设备上线,需要测试下。 + for (IotRuleSceneDO.TriggerConditionParameter parameter : condition.getParameters()) { + // 计算是否匹配 + IotRuleSceneTriggerConditionParameterOperatorEnum operator = + IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); + if (operator == null) { + log.error("[getMatchedRuleSceneList][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", + ruleScene.getId(), trigger, parameter.getOperator()); + return false; + } + Object messageValue = ((Map) message.getData()).get(parameter.getIdentifier()); + if (messageValue == null) { + return false; + } + String springExpression; + if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, + IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN)) { + String[] parameterValues = StrUtil.splitToArray(parameter.getValue(), CharPool.COMMA); + springExpression = String.format(operator.getSpringExpression(), messageValue, parameterValues[0], + messageValue, parameterValues[1]); + } else { + springExpression = String.format(operator.getSpringExpression(), messageValue, parameter.getValue()); + } + // TODO @芋艿:【需优化】需要考虑 struct、时间等参数的比较 + try { + System.out.println(SpringExpressionUtils.parseExpression(springExpression)); + } catch (Exception e) { + log.error("[getMatchedRuleSceneList][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}) 计算异常]", + message, ruleScene.getId(), trigger, springExpression, e); + } + } + return true; + }); + if (found == null) { + return false; + } + log.info("[getMatchedRuleSceneList][消息({}) 匹配到规则场景编号({}) 的触发器({})]", message, ruleScene.getId(), trigger); + return true; + } + return false; + }); + } + +} From 910bb6ca3c06eeae4d97b665ca61991722013bb0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Feb 2025 22:08:34 +0800 Subject: [PATCH 143/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=AE=8C=E5=96=84=20IotRuleScene?= =?UTF-8?q?ServiceImpl=20=E7=9A=84=E8=A7=84=E5=88=99=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=EF=BC=8CisTriggerConditionParameterMatched?= =?UTF-8?q?=20=E5=87=BD=E6=95=B0=E6=9C=89=E7=82=B9=E9=95=BF=EF=BC=8C=3D=20?= =?UTF-8?q?=3D=20=E6=8D=89=E6=91=B8=E5=92=8B=E4=BC=98=E5=8C=96=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/util/number/NumberUtils.java | 14 ++ .../util/spring/SpringExpressionUtils.java | 14 ++ ...TriggerConditionParameterOperatorEnum.java | 37 +++-- .../service/rule/IotRuleSceneServiceImpl.java | 138 +++++++++++++----- 4 files changed, 153 insertions(+), 50 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java index c928e2fcfd..c2787f46f1 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/NumberUtils.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.framework.common.util.number; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import java.math.BigDecimal; +import java.util.List; /** * 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能 @@ -20,6 +22,18 @@ public class NumberUtils { return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null; } + public static boolean isAllNumber(List values) { + if (CollUtil.isEmpty(values)) { + return false; + } + for (String value : values) { + if (!NumberUtil.isNumber(value)) { + return false; + } + } + return true; + } + /** * 通过经纬度获取地球上两点之间的距离 * diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java index 069e89db3d..abb1cece85 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringExpressionUtils.java @@ -97,12 +97,26 @@ public class SpringExpressionUtils { * @return 执行界面 */ public static Object parseExpression(String expressionString) { + return parseExpression(expressionString, null); + } + + /** + * 从 Bean 工厂,解析 EL 表达式的结果 + * + * @param expressionString EL 表达式 + * @param variables 变量 + * @return 执行界面 + */ + public static Object parseExpression(String expressionString, Map variables) { if (StrUtil.isBlank(expressionString)) { return null; } Expression expression = EXPRESSION_PARSER.parseExpression(expressionString); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext())); + if (MapUtil.isNotEmpty(variables)) { + context.setVariables(variables); + } return expression.getValue(context); } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java index f1a78cf31b..1aac8c2371 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java @@ -16,29 +16,42 @@ import java.util.Arrays; @Getter public enum IotRuleSceneTriggerConditionParameterOperatorEnum implements ArrayValuable { - EQUALS("=", "%s == %s"), - NOT_EQUALS("!=", "%s != %s"), + EQUALS("=", "#source == #value"), + NOT_EQUALS("!=", "!(#source == #value)"), - GREATER_THAN(">", "%s > %s"), - GREATER_THAN_OR_EQUALS(">=", "%s >= %s"), + GREATER_THAN(">", "#source > #value"), + GREATER_THAN_OR_EQUALS(">=", "#source >= #value"), - LESS_THAN("<", "%s < %s"), - LESS_THAN_OR_EQUALS("<=", "%s <= %s"), + LESS_THAN("<", "#source < #value"), + LESS_THAN_OR_EQUALS("<=", "#source <= #value"), - IN("in", "%s in { %s }"), - NOT_IN("not in", "%s not in { %s }"), + IN("in", "#values.contains(#source)"), + NOT_IN("not in", "!(#values.contains(#source))"), - BETWEEN("between", "(%s >= %s) && (%s <= %s)"), - NOT_BETWEEN("not between", "!(%s between %s and %s)"), + BETWEEN("between", "(#source >= #values.get(0)) && (#source <= #values.get(1))"), + NOT_BETWEEN("not between", "(#source < #values.get(0)) || (#source > #values.get(1))"), - LIKE("like", "%s like %s"), // 字符串匹配 - NOT_NULL("not null", ""); // 非空 + LIKE("like", "#source.contains(#value)"), // 字符串匹配 + NOT_NULL("not null", "#source != null && #source.length() > 0"); // 非空 private final String operator; private final String springExpression; public static final String[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneTriggerConditionParameterOperatorEnum::getOperator).toArray(String[]::new); + /** + * Spring 表达式 - 原始值 + */ + public static final String SPRING_EXPRESSION_SOURCE = "source"; + /** + * Spring 表达式 - 目标值 + */ + public static final String SPRING_EXPRESSION_VALUE = "value"; + /** + * Spring 表达式 - 目标值数组 + */ + public static final String SPRING_EXPRESSION_VALUE_List = "values"; + public static IotRuleSceneTriggerConditionParameterOperatorEnum operatorOf(String operator) { return ArrayUtil.firstMatch(item -> item.getOperator().equals(operator), values()); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index 0d8d0cdddf..90f893ef20 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -2,9 +2,12 @@ package cn.iocoder.yudao.module.iot.service.rule; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.CharPool; +import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; @@ -20,9 +23,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.HashMap; import java.util.List; import java.util.Map; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; /** @@ -48,10 +53,16 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { IotRuleSceneDO.Trigger trigger01 = new IotRuleSceneDO.Trigger(); trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType()); trigger01.setConditions(CollUtil.newArrayList()); + // 属性 IotRuleSceneDO.TriggerCondition condition01 = new IotRuleSceneDO.TriggerCondition(); condition01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); condition01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier()); condition01.setParameters(CollUtil.newArrayList()); +// IotRuleSceneDO.TriggerConditionParameter parameter010 = new IotRuleSceneDO.TriggerConditionParameter(); +// parameter010.setIdentifier("width"); +// parameter010.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); +// parameter010.setValue("abc"); +// condition01.getParameters().add(parameter010); IotRuleSceneDO.TriggerConditionParameter parameter011 = new IotRuleSceneDO.TriggerConditionParameter(); parameter011.setIdentifier("width"); parameter011.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); @@ -103,8 +114,23 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { parameter020.setValue("2,3"); condition01.getParameters().add(parameter020); trigger01.getConditions().add(condition01); + // 状态 + IotRuleSceneDO.TriggerCondition condition02 = new IotRuleSceneDO.TriggerCondition(); + condition02.setType(IotDeviceMessageTypeEnum.STATE.getType()); + condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()); + condition02.setParameters(CollUtil.newArrayList()); + trigger01.getConditions().add(condition02); + // TODO 芋艿:事件 + IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition(); + condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType()); + condition03.setIdentifier("xxx"); + condition03.setParameters(CollUtil.newArrayList()); + IotRuleSceneDO.TriggerConditionParameter parameter030 = new IotRuleSceneDO.TriggerConditionParameter(); + parameter030.setIdentifier("width"); + parameter030.setOperator(IotRuleSceneTriggerConditionParameterOperatorEnum.EQUALS.getOperator()); + parameter030.setValue("1"); + trigger01.getConditions().add(condition03); ruleScene01.getTriggers().add(trigger01); - return ListUtil.toList(ruleScene01); } @@ -139,7 +165,6 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { * @param message 设备消息 * @return 规则场景列表 */ - @SuppressWarnings("unchecked") private List getMatchedRuleSceneList(IotDeviceMessage message) { // 1. 匹配设备 // TODO @芋艿:可能需要 getSelf(); 缓存 @@ -152,54 +177,27 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { // 2. 匹配 trigger 触发器的条件 return filterList(ruleScenes, ruleScene -> { for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { - // 非设备触发,不匹配 + // 2.1 非设备触发,不匹配 if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) { return false; } // TODO 芋艿:产品、设备的匹配,要不要这里在做一次???貌似和 1. 部分重复了 - // 条件为空,说明没有匹配的条件,因此不匹配 + // 2.2 条件为空,说明没有匹配的条件,因此不匹配 if (CollUtil.isEmpty(trigger.getConditions())) { return false; } - IotRuleSceneDO.TriggerCondition found = CollUtil.findOne(trigger.getConditions(), condition -> { + // 2.3 多个条件,只需要满足一个即可 + IotRuleSceneDO.TriggerCondition matchedCondition = CollUtil.findOne(trigger.getConditions(), condition -> { if (ObjUtil.notEqual(message.getType(), condition.getType()) || ObjUtil.notEqual(message.getIdentifier(), condition.getIdentifier())) { return false; } - // TODO @芋艿:设备上线,需要测试下。 - for (IotRuleSceneDO.TriggerConditionParameter parameter : condition.getParameters()) { - // 计算是否匹配 - IotRuleSceneTriggerConditionParameterOperatorEnum operator = - IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); - if (operator == null) { - log.error("[getMatchedRuleSceneList][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", - ruleScene.getId(), trigger, parameter.getOperator()); - return false; - } - Object messageValue = ((Map) message.getData()).get(parameter.getIdentifier()); - if (messageValue == null) { - return false; - } - String springExpression; - if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, - IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN)) { - String[] parameterValues = StrUtil.splitToArray(parameter.getValue(), CharPool.COMMA); - springExpression = String.format(operator.getSpringExpression(), messageValue, parameterValues[0], - messageValue, parameterValues[1]); - } else { - springExpression = String.format(operator.getSpringExpression(), messageValue, parameter.getValue()); - } - // TODO @芋艿:【需优化】需要考虑 struct、时间等参数的比较 - try { - System.out.println(SpringExpressionUtils.parseExpression(springExpression)); - } catch (Exception e) { - log.error("[getMatchedRuleSceneList][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}) 计算异常]", - message, ruleScene.getId(), trigger, springExpression, e); - } - } - return true; + // 多个条件参数,必须全部满足。所以,下面的逻辑就是找到一个不满足的条件参数 + IotRuleSceneDO.TriggerConditionParameter notMatchedParameter = CollUtil.findOne(condition.getParameters(), + parameter -> !isTriggerConditionParameterMatched(message, parameter, ruleScene, trigger)); + return notMatchedParameter == null; }); - if (found == null) { + if (matchedCondition == null) { return false; } log.info("[getMatchedRuleSceneList][消息({}) 匹配到规则场景编号({}) 的触发器({})]", message, ruleScene.getId(), trigger); @@ -209,4 +207,68 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { }); } + // TODO @芋艿:【可优化】可以考虑增加下单测,边界太多了。 + /** + * 判断触发器的条件参数是否匹配 + * + * @param message 设备消息 + * @param parameter 触发器条件参数 + * @param ruleScene 规则场景(用于日志,无其它作用) + * @param trigger 触发器(用于日志,无其它作用) + * @return 是否匹配 + */ + @SuppressWarnings({"unchecked", "DataFlowIssue"}) + private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, + IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) { + // 计算是否匹配 + IotRuleSceneTriggerConditionParameterOperatorEnum operator = + IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); + if (operator == null) { + log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", + ruleScene.getId(), trigger, parameter.getOperator()); + return false; + } + // TODO @芋艿:目前只支持方便转换成 string 的类型,如果是 struct、list 这种,需要考虑下 + String messageValue = MapUtil.getStr((Map) message.getData(), parameter.getIdentifier()); + if (messageValue == null) { + return false; + } + Map springExpressionVariables = new HashMap<>(); + try { + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue); + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue()); + if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.IN, + IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN)) { + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, + StrUtil.split(parameter.getValue(), CharPool.COMMA)); + } else if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, + IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN)) { + List parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); + if (NumberUtil.isNumber(messageValue) && NumberUtils.isAllNumber(parameterValues)) { // 特殊:解决数字的比较 + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, + NumberUtil.parseDouble(messageValue)); + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, + convertList(parameterValues, NumberUtil::parseDouble)); + } else { + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, parameterValues); + } + } else if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN, + IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS, + IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN, + IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS)) { + if (NumberUtil.isNumber(messageValue) && NumberUtil.isNumber(parameter.getValue())) { // 特殊:解决数字的比较 + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, + NumberUtil.parseDouble(messageValue)); + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, + NumberUtil.parseDouble(parameter.getValue())); + } + } + return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables); + } catch (Exception e) { + log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]", + message, ruleScene.getId(), trigger, operator, springExpressionVariables, e); + return false; + } + } + } From 4f84182dabd8baafe750036dc4b7eefb45491e2e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Feb 2025 22:21:53 +0800 Subject: [PATCH 144/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E7=AE=80=E5=8C=96=20isTriggerCon?= =?UTF-8?q?ditionParameterMatched=20=E6=96=B9=E6=B3=95=EF=BC=8C=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E6=B3=A8=E9=87=8A=EF=BC=8C=E6=8F=90=E5=8D=87=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7=EF=BC=88=E8=99=BD=E7=84=B6=E5=A4=9A=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E8=AE=A1=E7=AE=97=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/rule/IotRuleSceneServiceImpl.java | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index 90f893ef20..3e120c648b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -220,7 +220,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @SuppressWarnings({"unchecked", "DataFlowIssue"}) private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) { - // 计算是否匹配 + // 1.1 校验操作符是否合法 IotRuleSceneTriggerConditionParameterOperatorEnum operator = IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); if (operator == null) { @@ -228,41 +228,36 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { ruleScene.getId(), trigger, parameter.getOperator()); return false; } - // TODO @芋艿:目前只支持方便转换成 string 的类型,如果是 struct、list 这种,需要考虑下 + // 1.2 校验消息是否包含对应的值 String messageValue = MapUtil.getStr((Map) message.getData(), parameter.getIdentifier()); if (messageValue == null) { return false; } + + // 2.1 构建 Spring 表达式的变量 Map springExpressionVariables = new HashMap<>(); try { springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue); springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, parameter.getValue()); - if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.IN, - IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_IN)) { - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, - StrUtil.split(parameter.getValue(), CharPool.COMMA)); - } else if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, - IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN)) { - List parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); - if (NumberUtil.isNumber(messageValue) && NumberUtils.isAllNumber(parameterValues)) { // 特殊:解决数字的比较 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, - NumberUtil.parseDouble(messageValue)); - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, - convertList(parameterValues, NumberUtil::parseDouble)); - } else { - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, parameterValues); - } - } else if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN, + List parameterValues = StrUtil.splitTrim(parameter.getValue(), CharPool.COMMA); + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, parameterValues); + // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题! + if (ObjectUtils.equalsAny(operator, IotRuleSceneTriggerConditionParameterOperatorEnum.BETWEEN, + IotRuleSceneTriggerConditionParameterOperatorEnum.NOT_BETWEEN, + IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN, IotRuleSceneTriggerConditionParameterOperatorEnum.GREATER_THAN_OR_EQUALS, IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN, - IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS)) { - if (NumberUtil.isNumber(messageValue) && NumberUtil.isNumber(parameter.getValue())) { // 特殊:解决数字的比较 - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, - NumberUtil.parseDouble(messageValue)); - springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, - NumberUtil.parseDouble(parameter.getValue())); - } + IotRuleSceneTriggerConditionParameterOperatorEnum.LESS_THAN_OR_EQUALS) + && NumberUtil.isNumber(messageValue) + && NumberUtils.isAllNumber(parameterValues)) { + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_SOURCE, + NumberUtil.parseDouble(messageValue)); + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE, + NumberUtil.parseDouble(parameter.getValue())); + springExpressionVariables.put(IotRuleSceneTriggerConditionParameterOperatorEnum.SPRING_EXPRESSION_VALUE_List, + convertList(parameterValues, NumberUtil::parseDouble)); } + // 2.2 计算 Spring 表达式 return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables); } catch (Exception e) { log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]", From 48cfcdadc13a642f28859912c7e808f0f9d80302 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Feb 2025 12:05:13 +0800 Subject: [PATCH 145/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=AE=9E=E7=8E=B0=E8=A7=84?= =?UTF-8?q?=E5=88=99=20IotRuleSceneDeviceControlAction=20=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/IotRuleSceneActionTypeEnum.java | 31 +++++++ .../dal/dataobject/device/IotDeviceDO.java | 3 +- .../dal/dataobject/rule/IotRuleSceneDO.java | 79 +++++++++--------- .../iot/dal/redis/RedisKeyConstants.java | 14 ++-- .../rule/IotRuleSceneMessageHandler.java | 2 +- .../iot/mq/consumer/rule/package-info.java | 4 - .../iot/mq/message/IotDeviceMessage.java | 5 ++ .../service/device/IotDeviceServiceImpl.java | 13 ++- .../control/IotDeviceDownstreamService.java | 4 +- .../IotDeviceDownstreamServiceImpl.java | 40 ++++++---- .../control/IotDeviceUpstreamServiceImpl.java | 38 ++++----- .../iot/service/rule/IotRuleSceneService.java | 7 +- .../service/rule/IotRuleSceneServiceImpl.java | 80 ++++++++++++++++--- .../rule/action/IotRuleSceneAction.java | 29 +++++++ .../IotRuleSceneDeviceControlAction.java | 56 +++++++++++++ .../thingmodel/IotThingModelServiceImpl.java | 14 ++-- .../src/main/resources/application.yaml | 2 + 17 files changed, 306 insertions(+), 115 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java new file mode 100644 index 0000000000..2dfb92f636 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Iot 规则场景的触发类型枚举 + * + * 设备触发,定时触发 + */ +@RequiredArgsConstructor +@Getter +public enum IotRuleSceneActionTypeEnum implements ArrayValuable { + + DEVICE_CONTROL(1), // 设备执行 + ALERT(2), // 告警执行 + DATA_BRIDGE(3); // 桥接执行 + + private final Integer type; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotRuleSceneActionTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index c146f8248d..351e001857 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -27,7 +28,7 @@ import java.util.Set; @Builder @NoArgsConstructor @AllArgsConstructor -public class IotDeviceDO extends BaseDO { +public class IotDeviceDO extends TenantBaseDO { /** * 设备 ID,主键,自增 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java index aa6c314253..88df1946e0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java @@ -1,12 +1,14 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -30,7 +32,7 @@ import java.util.Map; @Builder @NoArgsConstructor @AllArgsConstructor -public class IotRuleSceneDO extends BaseDO { +public class IotRuleSceneDO extends TenantBaseDO { /** * 场景编号 @@ -56,24 +58,26 @@ public class IotRuleSceneDO extends BaseDO { * 触发器数组 */ @TableField(typeHandler = JacksonTypeHandler.class) - private List triggers; + private List triggers; + + // TODO @芋艿:需要调研下 https://help.aliyun.com/zh/iot/user-guide/scene-orchestration-1?spm=a2c4g.11186623.help-menu-30520.d_2_4_5_0.45413908fxCSVa /** * 执行器数组 */ @TableField(typeHandler = JacksonTypeHandler.class) - private List actuators; + private List actions; /** - * 触发器 + * 触发器配置 */ @Data - public static class Trigger { + public static class TriggerConfig { /** * 触发类型 * - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum} + * 枚举 {@link IotRuleSceneTriggerTypeEnum} */ private Integer type; @@ -93,14 +97,15 @@ public class IotRuleSceneDO extends BaseDO { /** * 触发条件数组 * - * TODO @芋艿:注释说明 + * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时,必填 + * 条件与条件之间,是“或”的关系 */ private List conditions; /** * CRON 表达式 * - * TODO @芋艿:注释说明 + * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,必填 */ private String cronExpression; @@ -127,6 +132,8 @@ public class IotRuleSceneDO extends BaseDO { /** * 参数数组 + * + * 参数与参数之间,是“或”的关系 */ private List parameters; @@ -163,18 +170,41 @@ public class IotRuleSceneDO extends BaseDO { } /** - * 执行器 + * 执行器配置 */ @Data - public static class Actuator { + public static class ActionConfig { /** * 执行类型 * - * TODO @芋艿:control、alert、webhook(待定) + * 枚举 {@link IotRuleSceneActionTypeEnum} */ private Integer type; + /** + * 设备控制 + * + * 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时,必填 + */ + private ActionDeviceControl deviceControl; + + /** + * 数据桥接编号 + * + * 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时,必填 + * TODO 芋艿:关联 + */ + private Long dataBridgeId; + + } + + /** + * 执行设备控制 + */ + @Data + public static class ActionDeviceControl { + /** * 产品标识 * @@ -188,35 +218,12 @@ public class IotRuleSceneDO extends BaseDO { */ private List deviceNames; - /** - * 控制数组 - * - * TODO 芋艿:类型的情况下 - */ - private List controls; - - /** - * 数据桥接编号 - * - * TODO 芋艿:暂定! - * TODO 芋艿:关联 - */ - private Long bridgeId; - - } - - /** - * 执行器控制 - */ - @Data - public static class ActuatorControl { - /** * 消息类型 * * 枚举 {@link IotDeviceMessageTypeEnum#PROPERTY}、{@link IotDeviceMessageTypeEnum#SERVICE} */ - private Integer type; + private String type; /** * 消息标识符 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java index 8145ff757e..bd4d258f7a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java @@ -17,7 +17,7 @@ public interface RedisKeyConstants { * HASH KEY:identifier 属性标识 * VALUE 数据类型:String(JSON) {@link IotDevicePropertyDO} */ - String DEVICE_PROPERTY = "device_property:%s"; + String DEVICE_PROPERTY = "iot:device_property:%s"; /** * 设备的最后上报时间,采用 ZSET 结构 @@ -25,23 +25,23 @@ public interface RedisKeyConstants { * KEY 格式:{deviceKey} * SCORE:上报时间 */ - String DEVICE_REPORT_TIMES = "device_report_times"; + String DEVICE_REPORT_TIMES = "iot:device_report_times"; /** - * 设备信息的数据缓存,使用 Spring Cache 操作 + * 设备信息的数据缓存,使用 Spring Cache 操作(忽略租户) * * KEY 格式:device_${productKey}_${deviceKey} * VALUE 数据类型:String(JSON) */ - String DEVICE = "device"; + String DEVICE = "iot:device"; /** - * 物模型的数据缓存,使用 Spring Cache 操作 + * 物模型的数据缓存,使用 Spring Cache 操作(忽略租户) * * KEY 格式:thing_model_${productKey} * VALUE 数据类型:String 数组(JSON),即 {@link cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO} 列表 */ - String THING_MODEL_LIST = "thing_model_list"; + String THING_MODEL_LIST = "iot:thing_model_list"; /** * 设备插件的插件进程编号的映射,采用 HASH 结构 @@ -50,6 +50,6 @@ public interface RedisKeyConstants { * HASH KEY:${deviceKey} * VALUE:插件进程编号,对应 {@link IotPluginInstanceDO#getProcessId()} 字段 */ - String DEVICE_PLUGIN_INSTANCE_PROCESS_IDS = "device_plugin_instance_process_ids"; + String DEVICE_PLUGIN_INSTANCE_PROCESS_IDS = "iot:device_plugin_instance_process_ids"; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java index bdd148ec07..e6ea3e22d0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/IotRuleSceneMessageHandler.java @@ -24,7 +24,7 @@ public class IotRuleSceneMessageHandler { @Async public void onMessage(IotDeviceMessage message) { log.info("[onMessage][消息内容({})]", message); - ruleSceneService.executeRuleScene(message); + ruleSceneService.executeRuleSceneByDevice(message); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java deleted file mode 100644 index 3920443172..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/rule/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * TODO 芋艿:未来实现一个 IotRuleMessageConsumer - */ -package cn.iocoder.yudao.module.iot.mq.consumer.rule; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java index baf45fbab2..e4766755da 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java @@ -67,4 +67,9 @@ public class IotDeviceMessage { */ private LocalDateTime reportTime; + /** + * 租户编号 + */ + private Long tenantId; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 0b36b37bc1..2680f47d13 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; @@ -261,13 +262,9 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @Override - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { - // 保证在 @CacheEvict 之前,忽略租户 - return TenantUtils.executeIgnore(() -> getSelf().getDeviceByProductKeyAndDeviceNameFromCache0(productKey, deviceName)); - } - @Cacheable(value = RedisKeyConstants.DEVICE, key = "#productKey + '_' + #deviceName", unless = "#result == null") - public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache0(String productKey, String deviceName) { + @TenantIgnore // 忽略租户信息,跨租户 productKey + deviceName 是唯一的 + public IotDeviceDO getDeviceByProductKeyAndDeviceNameFromCache(String productKey, String deviceName) { return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); } @@ -389,8 +386,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { } private void deleteDeviceCache(IotDeviceDO device) { - // 保证在 @CacheEvict 之前,忽略租户 - TenantUtils.executeIgnore(() -> getSelf().deleteDeviceCache0(device)); + // 保证 Spring AOP 触发 + getSelf().deleteDeviceCache0(device); } private void deleteDeviceCache(List devices) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java index 433b15320b..f09604dea2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device.control; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import jakarta.validation.Valid; /** @@ -16,7 +17,8 @@ public interface IotDeviceDownstreamService { * 设备下行,可用于设备模拟 * * @param downstreamReqVO 设备下行请求 VO + * @return 下发消息 */ - void downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO); + IotDeviceMessage downstreamDevice(@Valid IotDeviceDownstreamReqVO downstreamReqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 6db8a76ab3..006636c230 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -54,7 +54,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic private IotDeviceProducer deviceProducer; @Override - public void downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) { + public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) { // 校验设备是否存在 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); // TODO 芋艿:父设备的处理 @@ -62,31 +62,28 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic // 服务调用 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) { - invokeDeviceService(downstreamReqVO, device, parentDevice); - return; + return invokeDeviceService(downstreamReqVO, device, parentDevice); } // 属性相关 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { // 属性设置 if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { - setDeviceProperty(downstreamReqVO, device, parentDevice); - return; + return setDeviceProperty(downstreamReqVO, device, parentDevice); } // 属性设置 if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { - getDeviceProperty(downstreamReqVO, device, parentDevice); - return; + return getDeviceProperty(downstreamReqVO, device, parentDevice); } } // 配置下发 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType()) && Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) { - setDeviceConfig(downstreamReqVO, device, parentDevice); - return; + return setDeviceConfig(downstreamReqVO, device, parentDevice); } // TODO 芋艿:ota 升级 + return null; } /** @@ -95,9 +92,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param downstreamReqVO 下行请求 * @param device 设备 * @param parentDevice 父设备 + * @return 下发消息 */ @SuppressWarnings("unchecked") - private void invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO, + private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO, IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { @@ -124,6 +122,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic device.getDeviceKey(), reqDTO, result); throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); } + return message; } /** @@ -132,10 +131,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param downstreamReqVO 下行请求 * @param device 设备 * @param parentDevice 父设备 + * @return 下发消息 */ @SuppressWarnings("unchecked") - private void setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + private IotDeviceMessage setDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); @@ -162,6 +162,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic device.getDeviceKey(), reqDTO, result); throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); } + return message; } /** @@ -170,10 +171,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param downstreamReqVO 下行请求 * @param device 设备 * @param parentDevice 父设备 + * @return 下发消息 */ @SuppressWarnings("unchecked") - private void getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + private IotDeviceMessage getDeviceProperty(IotDeviceDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof List)) { throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 List 类型"); @@ -200,6 +202,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic device.getDeviceKey(), reqDTO, result); throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); } + return message; } /** @@ -208,10 +211,11 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param downstreamReqVO 下行请求 * @param device 设备 * @param parentDevice 父设备 + * @return 下发消息 */ @SuppressWarnings({"unchecked", "unused"}) - private void setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + private IotDeviceMessage setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数转换,无需校验 Map config = JsonUtils.parseObject(device.getConfig(), Map.class); @@ -235,6 +239,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic device.getDeviceKey(), reqDTO, result); throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); } + return message; } /** @@ -275,7 +280,8 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device, Integer code) { // 1. 完善消息 message.setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName()) - .setDeviceKey(device.getDeviceKey()); + .setDeviceKey(device.getDeviceKey()) + .setTenantId(device.getTenantId()); Assert.notNull(message.getRequestId(), "requestId 不能为空"); if (message.getReportTime() == null) { message.setReportTime(LocalDateTime.now()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index 9a1276ef4b..1cbf3cd2a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -98,26 +98,27 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { updateReqDTO.getProductKey(), updateReqDTO.getDeviceName()); return; } - // 1.2 记录设备的最后时间 - updateDeviceLastTime(device, updateReqDTO); - // 1.3 当前状态一致,不处理 - if (Objects.equals(device.getState(), updateReqDTO.getState())) { - return; - } + TenantUtils.execute(device.getTenantId(), () -> { + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, updateReqDTO); + // 1.3 当前状态一致,不处理 + if (Objects.equals(device.getState(), updateReqDTO.getState())) { + return; + } - // 2. 更新设备状态 - TenantUtils.executeIgnore(() -> - deviceService.updateDeviceState(device.getId(), updateReqDTO.getState())); + // 2. 更新设备状态 + deviceService.updateDeviceState(device.getId(), updateReqDTO.getState()); - // 3. TODO 芋艿:子设备的关联 + // 3. TODO 芋艿:子设备的关联 - // 4. 发送设备消息 - IotDeviceMessage message = BeanUtils.toBean(updateReqDTO, IotDeviceMessage.class) - .setType(IotDeviceMessageTypeEnum.STATE.getType()) - .setIdentifier(ObjUtil.equals(updateReqDTO.getState(), IotDeviceStateEnum.ONLINE.getState()) - ? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier() - : IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier()); - sendDeviceMessage(message, device); + // 4. 发送设备消息 + IotDeviceMessage message = BeanUtils.toBean(updateReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.STATE.getType()) + .setIdentifier(ObjUtil.equals(updateReqDTO.getState(), IotDeviceStateEnum.ONLINE.getState()) + ? IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier() + : IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier()); + sendDeviceMessage(message, device); + }); } @Override @@ -174,7 +175,8 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { private void sendDeviceMessage(IotDeviceMessage message, IotDeviceDO device) { // 1. 完善消息 - message.setDeviceKey(device.getDeviceKey()); + message.setDeviceKey(device.getDeviceKey()) + .setTenantId(device.getTenantId()); if (StrUtil.isEmpty(message.getRequestId())) { message.setRequestId(IdUtil.fastSimpleUUID()); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java index d1ebfd4f17..afe67c03a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.rule; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import java.util.List; @@ -22,10 +23,12 @@ public interface IotRuleSceneService { List getRuleSceneListByProductKeyAndDeviceNameFromCache(String productKey, String deviceName); /** - * 执行规则场景 + * 基于 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 场景,执行规则场景 * * @param message 消息 */ - void executeRuleScene(IotDeviceMessage message); + void executeRuleSceneByDevice(IotDeviceMessage message); + + // TODO @芋艿:基于 timer 场景,执行规则场景 } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index 3e120c648b..e58821ea75 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -11,13 +11,16 @@ import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotRuleSceneMapper; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -43,6 +46,9 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @Resource private IotRuleSceneMapper ruleSceneMapper; + @Resource + private List ruleSceneActions; + // TODO 芋艿,缓存待实现 @Override @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略 @@ -50,7 +56,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { if (true) { IotRuleSceneDO ruleScene01 = new IotRuleSceneDO(); ruleScene01.setTriggers(CollUtil.newArrayList()); - IotRuleSceneDO.Trigger trigger01 = new IotRuleSceneDO.Trigger(); + IotRuleSceneDO.TriggerConfig trigger01 = new IotRuleSceneDO.TriggerConfig(); trigger01.setType(IotRuleSceneTriggerTypeEnum.DEVICE.getType()); trigger01.setConditions(CollUtil.newArrayList()); // 属性 @@ -120,7 +126,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { condition02.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier()); condition02.setParameters(CollUtil.newArrayList()); trigger01.getConditions().add(condition02); - // TODO 芋艿:事件 + // 事件 IotRuleSceneDO.TriggerCondition condition03 = new IotRuleSceneDO.TriggerCondition(); condition03.setType(IotDeviceMessageTypeEnum.EVENT.getType()); condition03.setIdentifier("xxx"); @@ -131,13 +137,28 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { parameter030.setValue("1"); trigger01.getConditions().add(condition03); ruleScene01.getTriggers().add(trigger01); + // 动作 + ruleScene01.setActions(CollUtil.newArrayList()); + IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType()); + IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); + actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); + actionDeviceControl01.setDeviceNames(ListUtil.of("small")); + actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); + actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); + actionDeviceControl01.setData(MapUtil.builder() + .put("power", 1) + .put("color", "red") + .build()); + action01.setDeviceControl(actionDeviceControl01); + ruleScene01.getActions().add(action01); return ListUtil.toList(ruleScene01); } List list = ruleSceneMapper.selectList(); // TODO @芋艿:需要考虑开启状态 return filterList(list, ruleScene -> { - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) { if (ObjUtil.notEqual(trigger.getProductKey(), productKey)) { continue; } @@ -151,12 +172,17 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { } @Override - public void executeRuleScene(IotDeviceMessage message) { - // 1. 获得设备匹配的规则场景 - List ruleScenes = getMatchedRuleSceneList(message); - if (CollUtil.isEmpty(ruleScenes)) { - return; - } + public void executeRuleSceneByDevice(IotDeviceMessage message) { + TenantUtils.execute(message.getTenantId(), () -> { + // 1. 获得设备匹配的规则场景 + List ruleScenes = getMatchedRuleSceneList(message); + if (CollUtil.isEmpty(ruleScenes)) { + return; + } + + // 2. 执行规则场景 + executeRuleSceneAction(message, ruleScenes); + }); } /** @@ -176,7 +202,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { // 2. 匹配 trigger 触发器的条件 return filterList(ruleScenes, ruleScene -> { - for (IotRuleSceneDO.Trigger trigger : ruleScene.getTriggers()) { + for (IotRuleSceneDO.TriggerConfig trigger : ruleScene.getTriggers()) { // 2.1 非设备触发,不匹配 if (ObjUtil.notEqual(trigger.getType(), IotRuleSceneTriggerTypeEnum.DEVICE.getType())) { return false; @@ -219,7 +245,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { */ @SuppressWarnings({"unchecked", "DataFlowIssue"}) private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotRuleSceneDO.TriggerConditionParameter parameter, - IotRuleSceneDO ruleScene, IotRuleSceneDO.Trigger trigger) { + IotRuleSceneDO ruleScene, IotRuleSceneDO.TriggerConfig trigger) { // 1.1 校验操作符是否合法 IotRuleSceneTriggerConditionParameterOperatorEnum operator = IotRuleSceneTriggerConditionParameterOperatorEnum.operatorOf(parameter.getOperator()); @@ -266,4 +292,36 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { } } + /** + * 执行规则场景的动作 + * + * @param message 设备消息 + * @param ruleScenes 规则场景列表 + */ + private void executeRuleSceneAction(IotDeviceMessage message, List ruleScenes) { + // 1. 遍历规则场景 + ruleScenes.forEach(ruleScene -> { + // 2. 遍历规则场景的动作 + ruleScene.getActions().forEach(actionConfig -> { + // 3.1 获取对应的动作 Action 数组 + List actions = filterList(ruleSceneActions, + action -> action.getType().getType().equals(actionConfig.getType())); + if (CollUtil.isEmpty(actions)) { + return; + } + // 3.2 执行动作 + actions.forEach(action -> { + try { + action.execute(message, actionConfig); + log.info("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 成功]", + message, ruleScene.getId(), actionConfig); + } catch (Exception e) { + log.error("[executeRuleSceneAction][消息({}) 规则场景编号({}) 的执行动作({}) 执行异常]", + message, ruleScene.getId(), actionConfig, e); + } + }); + }); + }); + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java new file mode 100644 index 0000000000..04020c1760 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.iot.service.rule.action; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; + +/** + * IOT 规则场景的场景执行器接口 + * + * @author 芋道源码 + */ +public interface IotRuleSceneAction { + + /** + * 执行场景 + * + * @param message 消息 + * @param config 配置 + */ + void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config); + + /** + * 获得类型 + * + * @return 类型 + */ + IotRuleSceneActionTypeEnum getType(); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java new file mode 100644 index 0000000000..d8fd76b5e7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDeviceControlAction.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.iot.service.rule.action; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * IoT 设备控制的 {@link IotRuleSceneAction} 实现类 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class IotRuleSceneDeviceControlAction implements IotRuleSceneAction { + + @Resource + private IotDeviceDownstreamService deviceDownstreamService; + @Resource + private IotDeviceService deviceService; + + @Override + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { + IotRuleSceneDO.ActionDeviceControl control = config.getDeviceControl(); + Assert.notNull(control, "设备控制配置不能为空"); + // 遍历每个设备,下发消息 + control.getDeviceNames().forEach(deviceName -> { + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(control.getProductKey(), deviceName); + if (device == null) { + log.error("[execute][message({}) config({}) 对应的设备不存在]", message, config); + return; + } + try { + IotDeviceMessage downstreamMessage = deviceDownstreamService.downstreamDevice(new IotDeviceDownstreamReqVO() + .setId(device.getId()).setType(control.getType()).setIdentifier(control.getIdentifier()) + .setData(control.getData())); + log.info("[execute][message({}) config({}) 下发消息({})成功]", message, config, downstreamMessage); + } catch (Exception e) { + log.error("[execute][message({}) config({}) 下发消息失败]", message, config, e); + } + }); + } + + @Override + public IotRuleSceneActionTypeEnum getType() { + return IotRuleSceneActionTypeEnum.DEVICE_CONTROL; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index 60673268c1..1b8cf79e0c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelParam; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; @@ -135,13 +135,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { } @Override - public List getThingModelListByProductKeyFromCache(String productKey) { - // 保证在 @CacheEvict 之前,忽略租户 - return TenantUtils.executeIgnore(() -> getSelf().getThingModelListByProductKeyFromCache0(productKey)); - } - @Cacheable(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") - public List getThingModelListByProductKeyFromCache0(String productKey) { + @TenantIgnore // 忽略租户信息,跨租户 productKey 是唯一的 + public List getThingModelListByProductKeyFromCache(String productKey) { return thingModelMapper.selectListByProductKey(productKey); } @@ -354,8 +350,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { } private void deleteThingModelListCache(String productKey) { - // 保证在 @CacheEvict 之前,忽略租户 - TenantUtils.executeIgnore(() -> getSelf().deleteThingModelListCache0(productKey)); + // 保证 Spring AOP 触发 + getSelf().deleteThingModelListCache0(productKey); } @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 523d16deac..28b1eb60a6 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -311,6 +311,8 @@ yudao: - mail_account - mail_template - sms_template + - iot:device + - iot:thing_model_list sms-code: # 短信验证码相关的配置项 expire-times: 10m send-frequency: 1m From 5e71d1fc851746c3366ce8f1a02f474a5ee434eb Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Feb 2025 13:25:47 +0800 Subject: [PATCH 146/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20IotDataBridg?= =?UTF-8?q?eDO=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81=E7=9A=84=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/rule/IotDataBridgDirectionEnum.java | 30 ++++ .../iot/enums/rule/IotDataBridgTypeEnum.java | 41 +++++ .../dal/dataobject/rule/IotDataBridgeDO.java | 140 ++++++++++++++++++ .../action/IotRuleSceneDataBridgeAction.java | 33 +++++ 4 files changed, 244 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java new file mode 100644 index 0000000000..ff4993d0ea --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Iot 数据桥接的方向枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum IotDataBridgDirectionEnum implements ArrayValuable { + + INPUT(1), // 输入 + OUTPUT(2); // 输出 + + private final Integer type; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgDirectionEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java new file mode 100644 index 0000000000..cdec8d7979 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Iot 数据桥接的类型枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum IotDataBridgTypeEnum implements ArrayValuable { + + HTTP(1), + TCP(2), + WEBSOCKET(3), + + MQTT(10), + + DATABASE(20), + REDIS(21), + + ROCKETMQ(30), + RABBITMQ(31), + KAFKA(32) + ; + + private final Integer type; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java new file mode 100644 index 0000000000..568377dd02 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -0,0 +1,140 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +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.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.*; + +import java.util.Map; + +/** + * IoT 数据桥梁 DO + * + * @author 芋道源码 + */ +@TableName("iot_data_bridge") +@KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotDataBridgeDO extends BaseDO { + + /** + * 桥梁编号 + */ + @TableId + private Long id; + /** + * 桥梁名称 + */ + private String name; + /** + * 桥梁描述 + */ + private String description; + /** + * 桥梁状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 桥梁方向 + * + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgDirectionEnum} + */ + private Integer direction; + + /** + * 桥梁类型 + * + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum} + */ + private Integer type; + + /** + * 桥梁配置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Config config; + + /** + * 文件客户端的配置 + * 不同实现的客户端,需要不同的配置,通过子类来定义 + * + * @author 芋道源码 + */ + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) + // @JsonTypeInfo 注解的作用,Jackson 多态 + // 1. 序列化到时数据库时,增加 @class 属性。 + // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 + public interface Config { + } + + /** + * HTTP 配置 + */ + @Data + public static class HttpConfig implements Config { + + /** + * 请求 URL + */ + private String url; + /** + * 请求方法 + */ + private String method; + /** + * 请求头 + */ + private Map headers; + /** + * 请求参数 + */ + private Map query; + /** + * 请求体 + */ + private String body; + + } + + /** + * MQTT 配置 + */ + @Data + public static class MqttConfig implements Config { + + /** + * MQTT 服务器地址 + */ + private String url; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 客户端编号 + */ + private String clientId; + /** + * 主题 + */ + private String topic; + + } + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java new file mode 100644 index 0000000000..7f77ef3f43 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.service.rule.action; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import org.springframework.stereotype.Component; + +/** + * IoT 数据桥梁的 {@link IotRuleSceneAction} 实现类 + * + * @author 芋道源码 + */ +@Component +public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { + + @Override + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { + // TODO @芋艿:http + // TODO @芋艿:mq-redis + // TODO @芋艿:mq-数据库 + // TODO @芋艿:kafka + // TODO @芋艿:rocketmq + // TODO @芋艿:rabbitmq + // TODO @芋艿:mqtt + // TODO @芋艿:tcp + } + + @Override + public IotRuleSceneActionTypeEnum getType() { + return IotRuleSceneActionTypeEnum.DATA_BRIDGE; + } + +} From 7168e60fddc4210691a4f93651b2f0c9c081b1f3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Feb 2025 18:33:43 +0800 Subject: [PATCH 147/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=AE=9E=E7=8E=B0=20IotRuleScene?= =?UTF-8?q?DataBridgeAction=20=E7=9A=84=20http=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/common/util/http/HttpUtils.java | 14 ++- .../common/util/servlet/ServletUtils.java | 4 + .../framework/excel/core/util/ExcelUtils.java | 5 +- .../file/core/utils/FileTypeUtils.java | 4 +- .../dal/mysql/rule/IotDataBridgeMapper.java | 9 ++ .../service/rule/IotDataBridgeService.java | 20 ++++ .../rule/IotDataBridgeServiceImpl.java | 45 +++++++++ .../service/rule/IotRuleSceneServiceImpl.java | 8 +- .../action/IotRuleSceneDataBridgeAction.java | 99 ++++++++++++++++++- .../sms/core/client/impl/AliyunSmsClient.java | 2 +- .../sms/core/client/impl/HuaweiSmsClient.java | 8 +- .../server/controller/DefaultController.java | 32 ++++-- 12 files changed, 229 insertions(+), 21 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java index 9e75113d0a..e1e0ac08e7 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java @@ -7,13 +7,15 @@ import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpResponse; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; -import jakarta.servlet.http.HttpServletRequest; import java.net.URI; +import java.net.URLEncoder; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; /** @@ -23,6 +25,16 @@ import java.util.Map; */ public class HttpUtils { + /** + * 编码 URL 参数 + * + * @param value 参数 + * @return 编码后的参数 + */ + public static String encodeUtf8(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + @SuppressWarnings("unchecked") public static String replaceUrlQuery(String url, String key, String value) { UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset()); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java index 12731edad6..905f5d5416 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/servlet/ServletUtils.java @@ -98,4 +98,8 @@ public class ServletUtils { return JakartaServletUtil.getParamMap(request); } + public static Map getHeaderMap(HttpServletRequest request) { + return JakartaServletUtil.getHeaderMap(request); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java index f5c4b83139..eb037d9e17 100644 --- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.excel.core.util; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.converters.longconverter.LongStringConverter; @@ -8,8 +9,6 @@ import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -40,7 +39,7 @@ public class ExcelUtils { .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 .sheet(sheetName).doWrite(data); // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 - response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name())); + response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); response.setContentType("application/vnd.ms-excel;charset=UTF-8"); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java index 8970c004cf..b25870fe82 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.infra.framework.file.core.utils; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import com.alibaba.ttl.TransmittableThreadLocal; import jakarta.servlet.http.HttpServletResponse; import lombok.SneakyThrows; import org.apache.tika.Tika; import java.io.IOException; -import java.net.URLEncoder; /** * 文件类型 Utils @@ -60,7 +60,7 @@ public class FileTypeUtils { */ public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException { // 设置 header 和 contentType - response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); + response.setHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); String contentType = getMineType(content, filename); response.setContentType(contentType); // 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java new file mode 100644 index 0000000000..076b341f00 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java @@ -0,0 +1,9 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.rule; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface IotDataBridgeMapper extends BaseMapperX { +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java new file mode 100644 index 0000000000..cbf0b36c23 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.service.rule; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; + +/** + * IoT 数据桥梁的 Service 接口 + * + * @author 芋道源码 + */ +public interface IotDataBridgeService { + + /** + * 获得指定数据桥梁 + * + * @param id 数据桥梁编号 + * @return 数据桥梁 + */ + IotDataBridgeDO getIotDataBridge(Long id); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java new file mode 100644 index 0000000000..7814fd9bbc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.service.rule; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataBridgeMapper; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Objects; + +/** + * IoT 数据桥梁的 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotDataBridgeServiceImpl implements IotDataBridgeService { + + @Resource + private IotDataBridgeMapper dataBridgeMapper; + + // TODO @芋艿:临时测试 + @Override + public IotDataBridgeDO getIotDataBridge(Long id) { + if (Objects.equals(id, 1L)) { + IotDataBridgeDO.HttpConfig config = new IotDataBridgeDO.HttpConfig() + .setUrl("http://127.0.0.1:48080/test") +// .setMethod("POST") + .setMethod("GET") + .setQuery(MapUtil.of("aaa", "bbb")) + .setHeaders(MapUtil.of("ccc", "ddd")) + .setBody(JsonUtils.toJsonString(MapUtil.of("eee", "fff"))); + return IotDataBridgeDO.builder().id(1L).name("芋道").description("芋道源码").status(0).direction(1) + .type(IotDataBridgTypeEnum.HTTP.getType()).config(config).build(); + } + return dataBridgeMapper.selectById(id); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index e58821ea75..fe9cd636b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -139,6 +139,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { ruleScene01.getTriggers().add(trigger01); // 动作 ruleScene01.setActions(CollUtil.newArrayList()); + // 设备控制 IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType()); IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); @@ -151,7 +152,12 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { .put("color", "red") .build()); action01.setDeviceControl(actionDeviceControl01); - ruleScene01.getActions().add(action01); +// ruleScene01.getActions().add(action01); // TODO 芋艿:先不测试了 + // 数据桥接(http) + IotRuleSceneDO.ActionConfig action02 = new IotRuleSceneDO.ActionConfig(); + action02.setType(IotRuleSceneActionTypeEnum.DATA_BRIDGE.getType()); + action02.setDataBridgeId(1L); + ruleScene01.getActions().add(action02); return ListUtil.toList(ruleScene01); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index 7f77ef3f43..7774368d28 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -1,9 +1,28 @@ package cn.iocoder.yudao.module.iot.service.rule.action; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.*; import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; /** * IoT 数据桥梁的 {@link IotRuleSceneAction} 实现类 @@ -11,11 +30,38 @@ import org.springframework.stereotype.Component; * @author 芋道源码 */ @Component +@Slf4j public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { + @Resource + private RestTemplate restTemplate; + + @Resource + private IotDataBridgeService dataBridgeService; + + private final ObjectMapper objectMapper = new ObjectMapper(); + @Override public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { - // TODO @芋艿:http + // 1. 获得数据桥梁 + Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空"); + IotDataBridgeDO dataBridge = dataBridgeService.getIotDataBridge(config.getDataBridgeId()); + if (dataBridge == null || dataBridge.getConfig() == null) { + log.error("[execute][message({}) config({}) 对应的数据桥梁不存在]", message, config); + return; + } + if (CommonStatusEnum.isDisable(dataBridge.getStatus())) { + log.info("[execute][message({}) config({}) 对应的数据桥梁({}) 状态为禁用]", message, config, dataBridge); + return; + } + + // 2.1 执行 HTTP 请求 + // TODO @芋艿:groovy 或者 javascript 实现数据的转换;可以考虑基于 hutool 的 ScriptUtil 做 + if (IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { + executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig()); + return; + } + // TODO @芋艿:mq-redis // TODO @芋艿:mq-数据库 // TODO @芋艿:kafka @@ -23,6 +69,7 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { // TODO @芋艿:rabbitmq // TODO @芋艿:mqtt // TODO @芋艿:tcp + // TODO @芋艿:websocket } @Override @@ -30,4 +77,54 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { return IotRuleSceneActionTypeEnum.DATA_BRIDGE; } + @SuppressWarnings({"unchecked", "deprecation"}) + private void executeHttp(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) { + String url = null; + HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); + HttpEntity requestEntity = null; + ResponseEntity responseEntity = null; + try { + // 1.1 构建 Header + HttpHeaders headers = new HttpHeaders(); + if (CollUtil.isNotEmpty(config.getHeaders())) { + config.getHeaders().putAll(config.getHeaders()); + } + headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); + // 1.2 构建 URL + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); + if (CollUtil.isNotEmpty(config.getQuery())) { + config.getQuery().forEach(uriBuilder::queryParam); + } + // 1.3 构建请求体 + if (method == HttpMethod.GET) { + uriBuilder.queryParam("message", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message))); + url = uriBuilder.build().toUriString(); + requestEntity = new HttpEntity<>(headers); + } else { + url = uriBuilder.build().toUriString(); + Map requestBody = JsonUtils.parseObject(config.getBody(), Map.class); + if (requestBody == null) { + requestBody = new HashMap<>(); + } + requestBody.put("message", message); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); + requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); + } + + // 2.1 发送请求 + responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); + // 2.2 记录日志 + if (responseEntity.getStatusCode().is2xxSuccessful()) { + log.info("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", + message, config, url, method, requestEntity, responseEntity); + } else { + log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", + message, config, url, method, requestEntity, responseEntity); + } + } catch (Exception e) { + log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", + message, config, url, method, requestEntity, responseEntity, e); + } + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java index 558dbdef27..3dd12491a9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java @@ -184,7 +184,7 @@ public class AliyunSmsClient extends AbstractSmsClient { @SneakyThrows private static String percentCode(String str) { Assert.notNull(str, "str 不能为空"); - return URLEncoder.encode(str, StandardCharsets.UTF_8.name()) + return HttpUtils.encodeUtf8(str) .replace("+", "%20") // 加号 "+" 被替换为 "%20" .replace("*", "%2A") // 星号 "*" 被替换为 "%2A" .replace("%7E", "~"); // 波浪号 "%7E" 被替换为 "~" diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java index 82f55395e8..622f8ac1b9 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.format.FastDateFormat; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.http.HttpUtil; @@ -19,8 +18,6 @@ import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditS import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; import lombok.extern.slf4j.Slf4j; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDateTime; @@ -156,10 +153,9 @@ public class HuaweiSmsClient extends AbstractSmsClient { .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(null); } - @SuppressWarnings("CharsetObjectCanBeUsed") - private static void appendToBody(StringBuilder body, String key, String value) throws UnsupportedEncodingException { + private static void appendToBody(StringBuilder body, String key, String value) { if (StrUtil.isNotEmpty(value)) { - body.append(key).append(URLEncoder.encode(value, CharsetUtil.CHARSET_UTF_8.name())); + body.append(key).append(HttpUtils.encodeUtf8(value)); } } diff --git a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java index 2bf6e52775..f0e3878334 100644 --- a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java +++ b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.server.controller; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import jakarta.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -13,6 +17,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC * @author 芋道源码 */ @RestController +@Slf4j public class DefaultController { @RequestMapping("/admin-api/bpm/**") @@ -27,9 +32,9 @@ public class DefaultController { "[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]"); } - @RequestMapping(value = {"/admin-api/product/**", // 商品中心 + @RequestMapping(value = { "/admin-api/product/**", // 商品中心 "/admin-api/trade/**", // 交易中心 - "/admin-api/promotion/**"}) // 营销中心 + "/admin-api/promotion/**" }) // 营销中心 public CommonResult mall404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[商城系统 yudao-module-mall - 已禁用][参考 https://doc.iocoder.cn/mall/build/ 开启]"); @@ -47,28 +52,43 @@ public class DefaultController { "[CRM 模块 yudao-module-crm - 已禁用][参考 https://doc.iocoder.cn/crm/build/ 开启]"); } - @RequestMapping(value = {"/admin-api/report/**"}) + @RequestMapping(value = { "/admin-api/report/**"}) public CommonResult report404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[报表模块 yudao-module-report - 已禁用][参考 https://doc.iocoder.cn/report/ 开启]"); } - @RequestMapping(value = {"/admin-api/pay/**"}) + @RequestMapping(value = { "/admin-api/pay/**"}) public CommonResult pay404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[支付模块 yudao-module-pay - 已禁用][参考 https://doc.iocoder.cn/pay/build/ 开启]"); } - @RequestMapping(value = {"/admin-api/ai/**"}) + @RequestMapping(value = { "/admin-api/ai/**"}) public CommonResult ai404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[AI 大模型 yudao-module-ai - 已禁用][参考 https://doc.iocoder.cn/ai/build/ 开启]"); } - @RequestMapping(value = {"/admin-api/iot/**"}) + @RequestMapping(value = { "/admin-api/iot/**"}) public CommonResult iot404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[IOT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]"); } + /** + * 测试接口:打印 query、header、body + */ + @RequestMapping(value = { "/test" }) + @PermitAll + public CommonResult test(HttpServletRequest request) { + // 打印查询参数 + log.info("Query: {}", ServletUtils.getParamMap(request)); + // 打印请求头 + log.info("Header: {}", ServletUtils.getHeaderMap(request)); + // 打印请求体 + log.info("Body: {}", ServletUtils.getBody(request)); + return CommonResult.success(true); + } + } From 2109449a896e465e1740f88315864ed4c00ec298 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Feb 2025 20:06:18 +0800 Subject: [PATCH 148/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=E6=9C=89?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E7=9A=84=20Bridge=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=80=9D=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/rule/action/IotRuleSceneDataBridgeAction.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index 7774368d28..eb50fb5df1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; @@ -29,6 +28,9 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ * * @author 芋道源码 */ +// TODO @芋艿:【优化】因为 bridge 会比较多,所以可以考虑在 rule 下,新建一个 bridge 的 package,然后定义一个 bridgehandler,它有: +// 1. input 方法、output 方法 +// 2. build 方法,用于有状态的连接,例如说 mq、tcp、websocket @Component @Slf4j public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { @@ -39,8 +41,6 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { @Resource private IotDataBridgeService dataBridgeService; - private final ObjectMapper objectMapper = new ObjectMapper(); - @Override public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { // 1. 获得数据桥梁 @@ -62,6 +62,7 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { return; } + // TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; // TODO @芋艿:mq-redis // TODO @芋艿:mq-数据库 // TODO @芋艿:kafka From 8d0caaa16c68e9fe9718f524a2145051c4aed46c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Feb 2025 00:12:08 +0800 Subject: [PATCH 149/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=9F=BA=E4=BA=8E=20Quartz=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20IotSchedulerManager=EF=BC=81=E4=B8=BA?= =?UTF-8?q?=E4=BA=86=E5=85=BC=E5=AE=B9=20boot=20=E5=92=8C=20cloud=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/rule/IotRuleSceneDO.java | 12 +- .../job/config/IotJobConfiguration.java | 24 +++ .../job/core/IotSchedulerManager.java | 191 ++++++++++++++++++ .../service/rule/IotRuleSceneServiceImpl.java | 29 +++ .../rule/action/IotRuleSceneAction.java | 8 +- .../action/IotRuleSceneDataBridgeAction.java | 6 +- 6 files changed, 260 insertions(+), 10 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/config/IotJobConfiguration.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java index 88df1946e0..3c6ae6288e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java @@ -60,8 +60,6 @@ public class IotRuleSceneDO extends TenantBaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private List triggers; - // TODO @芋艿:需要调研下 https://help.aliyun.com/zh/iot/user-guide/scene-orchestration-1?spm=a2c4g.11186623.help-menu-30520.d_2_4_5_0.45413908fxCSVa - /** * 执行器数组 */ @@ -97,7 +95,7 @@ public class IotRuleSceneDO extends TenantBaseDO { /** * 触发条件数组 * - * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时,必填 + * 必填:当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#DEVICE} 时 * 条件与条件之间,是“或”的关系 */ private List conditions; @@ -105,7 +103,7 @@ public class IotRuleSceneDO extends TenantBaseDO { /** * CRON 表达式 * - * 当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时,必填 + * 必填:当 {@link #type} 为 {@link IotRuleSceneTriggerTypeEnum#TIMER} 时 */ private String cronExpression; @@ -185,15 +183,15 @@ public class IotRuleSceneDO extends TenantBaseDO { /** * 设备控制 * - * 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时,必填 + * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DEVICE_CONTROL} 时 */ private ActionDeviceControl deviceControl; /** * 数据桥接编号 * - * 当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时,必填 - * TODO 芋艿:关联 + * 必填:当 {@link #type} 为 {@link IotRuleSceneActionTypeEnum#DATA_BRIDGE} 时 + * 关联:{@link IotDataBridgeDO#getId()} */ private Long dataBridgeId; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/config/IotJobConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/config/IotJobConfiguration.java new file mode 100644 index 0000000000..7cd6f0961a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/config/IotJobConfiguration.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.iot.framework.job.config; + +import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +/** + * IoT 模块的 Job 自动配置类 + * + * @author 芋道源码 + */ +@Configuration +public class IotJobConfiguration { + + @Bean(initMethod = "start", destroyMethod = "stop") + public IotSchedulerManager iotSchedulerManager(DataSource dataSource, + ApplicationContext applicationContext) { + return new IotSchedulerManager(dataSource, applicationContext); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java new file mode 100644 index 0000000000..89eaaed1f4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java @@ -0,0 +1,191 @@ +package cn.iocoder.yudao.module.iot.framework.job.core; + +import cn.iocoder.yudao.framework.quartz.core.enums.JobDataKeyEnum; +import lombok.extern.slf4j.Slf4j; +import org.quartz.*; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +import javax.sql.DataSource; +import java.util.Map; +import java.util.Properties; + +/** + * IoT 模块的 Scheduler 管理类,基于 Quartz 实现 + * + * 疑问:为什么 IoT 模块不复用全局的 SchedulerManager 呢? + * 回复:yudao-cloud 项目,使用的是 XXL-Job 作为调度中心,无法动态添加任务。 + * + * @author 芋道源码 + */ +@Slf4j +public class IotSchedulerManager { + + private static final String SCHEDULER_NAME = "iotScheduler"; + + private final SchedulerFactoryBean schedulerFactoryBean; + + private Scheduler scheduler; + + public IotSchedulerManager(DataSource dataSource, + ApplicationContext applicationContext) { + // 1. 参考 SchedulerFactoryBean 类 + SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); + SpringBeanJobFactory jobFactory = new SpringBeanJobFactory(); + jobFactory.setApplicationContext(applicationContext); + schedulerFactoryBean.setJobFactory(jobFactory); + schedulerFactoryBean.setAutoStartup(true); + schedulerFactoryBean.setSchedulerName(SCHEDULER_NAME); + schedulerFactoryBean.setDataSource(dataSource); + schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(true); + Properties properties = new Properties(); + schedulerFactoryBean.setQuartzProperties(properties); + // 2. 参考 application-local.yaml 配置文件 + // 2.1 Scheduler 相关配置 + properties.put("org.quartz.scheduler.instanceName", SCHEDULER_NAME); + properties.put("org.quartz.scheduler.instanceId", "AUTO"); + // 2.2 JobStore 相关配置 + properties.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); + properties.put("org.quartz.jobStore.isClustered", "true"); + properties.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); + properties.put("org.quartz.jobStore.misfireThreshold", "60000"); + // 2.3 线程池相关配置 + properties.put("org.quartz.threadPool.threadCount", "25"); + properties.put("org.quartz.threadPool.threadPriority", "5"); + properties.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); + this.schedulerFactoryBean = schedulerFactoryBean; + } + + public void start() throws Exception { + log.info("[start][Scheduler 初始化开始]"); + // 初始化 + schedulerFactoryBean.afterPropertiesSet(); + schedulerFactoryBean.start(); + // 获得 Scheduler 对象 + this.scheduler = schedulerFactoryBean.getScheduler(); + log.info("[start][Scheduler 初始化完成]"); + } + + public void stop() { + log.info("[stop][Scheduler 关闭开始]"); + schedulerFactoryBean.stop(); + this.scheduler = null; + log.info("[stop][Scheduler 关闭完成]"); + } + + // ========== 参考 SchedulerManager 实现 ========== + + /** + * 添加或更新 Job 到 Quartz 中 + * + * @param jobClass 任务处理器的类 + * @param jobHandlerName 任务处理器的名字 + * @param cronExpression CRON 表达式 + * @param jobDataMap 任务数据 + * @throws SchedulerException 添加异常 + */ + public void addOrUpdateJob(Class jobClass, String jobHandlerName, + String cronExpression, Map jobDataMap) + throws SchedulerException { + if (scheduler.checkExists(new JobKey(jobHandlerName))) { + this.updateJob(jobHandlerName, cronExpression); + } else { + this.addJob(jobClass, jobHandlerName, cronExpression, jobDataMap); + } + } + + /** + * 添加 Job 到 Quartz 中 + * + * @param jobClass 任务处理器的类 + * @param jobHandlerName 任务处理器的名字 + * @param cronExpression CRON 表达式 + * @param jobDataMap 任务数据 + * @throws SchedulerException 添加异常 + */ + public void addJob(Class jobClass, String jobHandlerName, + String cronExpression, Map jobDataMap) + throws SchedulerException { + // 创建 JobDetail 对象 + JobDetail jobDetail = JobBuilder.newJob(jobClass) + .usingJobData(new JobDataMap(jobDataMap)) + .withIdentity(jobHandlerName).build(); + // 创建 Trigger 对象 + Trigger trigger = this.buildTrigger(jobHandlerName, cronExpression); + // 新增 Job 调度 + scheduler.scheduleJob(jobDetail, trigger); + } + + /** + * 更新 Job 到 Quartz + * + * @param jobHandlerName 任务处理器的名字 + * @param cronExpression CRON 表达式 + * @throws SchedulerException 更新异常 + */ + public void updateJob(String jobHandlerName, String cronExpression) + throws SchedulerException { + // 创建新 Trigger 对象 + Trigger newTrigger = this.buildTrigger(jobHandlerName, cronExpression); + // 修改调度 + scheduler.rescheduleJob(new TriggerKey(jobHandlerName), newTrigger); + } + + /** + * 删除 Quartz 中的 Job + * + * @param jobHandlerName 任务处理器的名字 + * @throws SchedulerException 删除异常 + */ + public void deleteJob(String jobHandlerName) throws SchedulerException { + // 暂停 Trigger 对象 + scheduler.pauseTrigger(new TriggerKey(jobHandlerName)); + // 取消并删除 Job 调度 + scheduler.unscheduleJob(new TriggerKey(jobHandlerName)); + scheduler.deleteJob(new JobKey(jobHandlerName)); + } + + /** + * 暂停 Quartz 中的 Job + * + * @param jobHandlerName 任务处理器的名字 + * @throws SchedulerException 暂停异常 + */ + public void pauseJob(String jobHandlerName) throws SchedulerException { + scheduler.pauseJob(new JobKey(jobHandlerName)); + } + + /** + * 启动 Quartz 中的 Job + * + * @param jobHandlerName 任务处理器的名字 + * @throws SchedulerException 启动异常 + */ + public void resumeJob(String jobHandlerName) throws SchedulerException { + scheduler.resumeJob(new JobKey(jobHandlerName)); + scheduler.resumeTrigger(new TriggerKey(jobHandlerName)); + } + + /** + * 立即触发一次 Quartz 中的 Job + * + * @param jobHandlerName 任务处理器的名字 + * @throws SchedulerException 触发异常 + */ + public void triggerJob(String jobHandlerName) + throws SchedulerException { + // 触发任务 + JobDataMap data = new JobDataMap(); + data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName); + scheduler.triggerJob(new JobKey(jobHandlerName), data); + } + + private Trigger buildTrigger(String jobHandlerName, String cronExpression) { + return TriggerBuilder.newTrigger() + .withIdentity(jobHandlerName) + .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)) + .build(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index fe9cd636b0..71c080d6a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -23,6 +23,11 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerKey; +import org.quartz.impl.StdSchedulerFactory; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -330,4 +335,28 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { }); } + // TODO @芋艿:测试思路代码,记得删除!!! + // 1. Job 类:IotRuleSceneJob + // 2. 参数:id + // 3. jobHandlerName:IotRuleSceneJob + id + + // 新增:addJob + // 修改:不存在 addJob、存在 updateJob + // 有 + 禁用:1)存在、停止;2)不存在:不处理;TODO 测试:直接暂停,是否可以???(结论:可以) + // 有 + 开启:1)存在,更新;2)不存在,新增; + // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以) + + public static void main2(String[] args) throws SchedulerException { +// System.out.println(QuartzJobBean.class); + Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); + scheduler.start(); + + String jobHandlerName = "123"; + // 暂停 Trigger 对象 + scheduler.pauseTrigger(new TriggerKey(jobHandlerName)); + // 取消并删除 Job 调度 + scheduler.unscheduleJob(new TriggerKey(jobHandlerName)); + scheduler.deleteJob(new JobKey(jobHandlerName)); + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java index 04020c1760..1cb17c9b44 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java @@ -4,6 +4,8 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import javax.annotation.Nullable; + /** * IOT 规则场景的场景执行器接口 * @@ -14,10 +16,12 @@ public interface IotRuleSceneAction { /** * 执行场景 * - * @param message 消息 + * @param message 消息,允许空 + * 1. 空的情况:定时触发 + * 2. 非空的情况:设备触发 * @param config 配置 */ - void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config); + void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config); /** * 获得类型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index eb50fb5df1..f7f3747e78 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -43,7 +43,11 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { @Override public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { - // 1. 获得数据桥梁 + // 1.1 如果消息为空,直接返回 + if (message == null) { + return; + } + // 1.2 获得数据桥梁 Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空"); IotDataBridgeDO dataBridge = dataBridgeService.getIotDataBridge(config.getDataBridgeId()); if (dataBridge == null || dataBridge.getConfig() == null) { From f6f162ad2fb7676e46b9747a661ed17427847292 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Feb 2025 12:39:56 +0800 Subject: [PATCH 150/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20IotRuleScene?= =?UTF-8?q?Job=20=E6=89=A7=E8=A1=8C=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/rule/IotRuleSceneController.java | 27 +++++ .../job/core/IotSchedulerManager.java | 62 ++++++------ .../module/iot/job/rule/IotRuleSceneJob.java | 57 +++++++++++ .../iot/service/rule/IotRuleSceneService.java | 12 ++- .../service/rule/IotRuleSceneServiceImpl.java | 99 ++++++++++++++++--- 5 files changed, 213 insertions(+), 44 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java new file mode 100644 index 0000000000..04e2f4570a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule; + +import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "管理后台 - IoT 规则场景") +@RestController +@RequestMapping("/iot/rule-scene") +@Validated +public class IotRuleSceneController { + + @Resource + private IotRuleSceneService ruleSceneService; + + @GetMapping("/test") + @PermitAll + public void test() { + ruleSceneService.test(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java index 89eaaed1f4..c52164b6e9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java @@ -80,18 +80,18 @@ public class IotSchedulerManager { * 添加或更新 Job 到 Quartz 中 * * @param jobClass 任务处理器的类 - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @param cronExpression CRON 表达式 * @param jobDataMap 任务数据 * @throws SchedulerException 添加异常 */ - public void addOrUpdateJob(Class jobClass, String jobHandlerName, + public void addOrUpdateJob(Class jobClass, String jobName, String cronExpression, Map jobDataMap) throws SchedulerException { - if (scheduler.checkExists(new JobKey(jobHandlerName))) { - this.updateJob(jobHandlerName, cronExpression); + if (scheduler.checkExists(new JobKey(jobName))) { + this.updateJob(jobName, cronExpression); } else { - this.addJob(jobClass, jobHandlerName, cronExpression, jobDataMap); + this.addJob(jobClass, jobName, cronExpression, jobDataMap); } } @@ -99,20 +99,20 @@ public class IotSchedulerManager { * 添加 Job 到 Quartz 中 * * @param jobClass 任务处理器的类 - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @param cronExpression CRON 表达式 * @param jobDataMap 任务数据 * @throws SchedulerException 添加异常 */ - public void addJob(Class jobClass, String jobHandlerName, + public void addJob(Class jobClass, String jobName, String cronExpression, Map jobDataMap) throws SchedulerException { // 创建 JobDetail 对象 JobDetail jobDetail = JobBuilder.newJob(jobClass) .usingJobData(new JobDataMap(jobDataMap)) - .withIdentity(jobHandlerName).build(); + .withIdentity(jobName).build(); // 创建 Trigger 对象 - Trigger trigger = this.buildTrigger(jobHandlerName, cronExpression); + Trigger trigger = this.buildTrigger(jobName, cronExpression); // 新增 Job 调度 scheduler.scheduleJob(jobDetail, trigger); } @@ -120,70 +120,70 @@ public class IotSchedulerManager { /** * 更新 Job 到 Quartz * - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @param cronExpression CRON 表达式 * @throws SchedulerException 更新异常 */ - public void updateJob(String jobHandlerName, String cronExpression) + public void updateJob(String jobName, String cronExpression) throws SchedulerException { // 创建新 Trigger 对象 - Trigger newTrigger = this.buildTrigger(jobHandlerName, cronExpression); + Trigger newTrigger = this.buildTrigger(jobName, cronExpression); // 修改调度 - scheduler.rescheduleJob(new TriggerKey(jobHandlerName), newTrigger); + scheduler.rescheduleJob(new TriggerKey(jobName), newTrigger); } /** * 删除 Quartz 中的 Job * - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @throws SchedulerException 删除异常 */ - public void deleteJob(String jobHandlerName) throws SchedulerException { + public void deleteJob(String jobName) throws SchedulerException { // 暂停 Trigger 对象 - scheduler.pauseTrigger(new TriggerKey(jobHandlerName)); + scheduler.pauseTrigger(new TriggerKey(jobName)); // 取消并删除 Job 调度 - scheduler.unscheduleJob(new TriggerKey(jobHandlerName)); - scheduler.deleteJob(new JobKey(jobHandlerName)); + scheduler.unscheduleJob(new TriggerKey(jobName)); + scheduler.deleteJob(new JobKey(jobName)); } /** * 暂停 Quartz 中的 Job * - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @throws SchedulerException 暂停异常 */ - public void pauseJob(String jobHandlerName) throws SchedulerException { - scheduler.pauseJob(new JobKey(jobHandlerName)); + public void pauseJob(String jobName) throws SchedulerException { + scheduler.pauseJob(new JobKey(jobName)); } /** * 启动 Quartz 中的 Job * - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @throws SchedulerException 启动异常 */ - public void resumeJob(String jobHandlerName) throws SchedulerException { - scheduler.resumeJob(new JobKey(jobHandlerName)); - scheduler.resumeTrigger(new TriggerKey(jobHandlerName)); + public void resumeJob(String jobName) throws SchedulerException { + scheduler.resumeJob(new JobKey(jobName)); + scheduler.resumeTrigger(new TriggerKey(jobName)); } /** * 立即触发一次 Quartz 中的 Job * - * @param jobHandlerName 任务处理器的名字 + * @param jobName 任务名 * @throws SchedulerException 触发异常 */ - public void triggerJob(String jobHandlerName) + public void triggerJob(String jobName) throws SchedulerException { // 触发任务 JobDataMap data = new JobDataMap(); - data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName); - scheduler.triggerJob(new JobKey(jobHandlerName), data); + data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobName); + scheduler.triggerJob(new JobKey(jobName), data); } - private Trigger buildTrigger(String jobHandlerName, String cronExpression) { + private Trigger buildTrigger(String jobName, String cronExpression) { return TriggerBuilder.newTrigger() - .withIdentity(jobHandlerName) + .withIdentity(jobName) .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)) .build(); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java new file mode 100644 index 0000000000..2cda2fc20b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.job.rule; + +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.quartz.JobExecutionContext; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.util.Map; + +/** + * IoT 规则场景 Job,用于执行 {@link IotRuleSceneTriggerTypeEnum#TIMER} 类型的规则场景 + * + * @author 芋道源码 + */ +@Slf4j +public class IotRuleSceneJob extends QuartzJobBean { + + /** + * JobData Key - 规则场景编号 + */ + public static final String JOB_DATA_KEY_RULE_SCENE_ID = "ruleSceneId"; + + @Resource + private IotRuleSceneService ruleSceneService; + + @Override + protected void executeInternal(JobExecutionContext context) { + // 获得规则场景编号 + Long ruleSceneId = context.getMergedJobDataMap().getLong(JOB_DATA_KEY_RULE_SCENE_ID); + + // 执行规则场景 + ruleSceneService.executeRuleSceneByTimer(ruleSceneId); + } + + /** + * 创建 JobData Map + * + * @param ruleSceneId 规则场景编号 + * @return JobData Map + */ + public static Map buildJobDataMap(Long ruleSceneId) { + return Map.of(JOB_DATA_KEY_RULE_SCENE_ID, ruleSceneId); + } + + /** + * 创建 Job 名字 + * + * @param ruleSceneId 规则场景编号 + * @return Job 名字 + */ + public static String buildJobName(Long ruleSceneId) { + return String.format("%s_%d", IotRuleSceneJob.class.getSimpleName(), ruleSceneId); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java index afe67c03a3..6927b11725 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneService.java @@ -29,6 +29,16 @@ public interface IotRuleSceneService { */ void executeRuleSceneByDevice(IotDeviceMessage message); - // TODO @芋艿:基于 timer 场景,执行规则场景 + /** + * 基于 {@link IotRuleSceneTriggerTypeEnum#TIMER} 场景,执行规则场景 + * + * @param id 场景编号 + */ + void executeRuleSceneByTimer(Long id); + + /** + * TODO 芋艿:测试方法,需要删除 + */ + void test(); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index 71c080d6a0..7f8ff51f10 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.text.CharPool; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; @@ -19,9 +20,12 @@ import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerConditionParameterOperatorEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; +import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; +import cn.iocoder.yudao.module.iot.job.rule.IotRuleSceneJob; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.rule.action.IotRuleSceneAction; import jakarta.annotation.Resource; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.quartz.JobKey; import org.quartz.Scheduler; @@ -54,6 +58,9 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { @Resource private List ruleSceneActions; + @Resource(name = "iotSchedulerManager") + private IotSchedulerManager schedulerManager; + // TODO 芋艿,缓存待实现 @Override @TenantIgnore // 忽略租户隔离:因为 IotRuleSceneMessageHandler 调用时,一般未传递租户,所以需要忽略 @@ -186,7 +193,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { public void executeRuleSceneByDevice(IotDeviceMessage message) { TenantUtils.execute(message.getTenantId(), () -> { // 1. 获得设备匹配的规则场景 - List ruleScenes = getMatchedRuleSceneList(message); + List ruleScenes = getMatchedRuleSceneListByMessage(message); if (CollUtil.isEmpty(ruleScenes)) { return; } @@ -196,13 +203,60 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { }); } + @Override + public void executeRuleSceneByTimer(Long id) { + // 1.1 获得规则场景 +// IotRuleSceneDO scene = TenantUtils.executeIgnore(() -> ruleSceneMapper.selectById(id)); + // TODO @芋艿:这里,临时测试,后续删除。 + IotRuleSceneDO scene = new IotRuleSceneDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + if (true) { + scene.setTenantId(1L); + IotRuleSceneDO.TriggerConfig triggerConfig = new IotRuleSceneDO.TriggerConfig(); + triggerConfig.setType(IotRuleSceneTriggerTypeEnum.TIMER.getType()); + scene.setTriggers(ListUtil.toList(triggerConfig)); + // 动作 + IotRuleSceneDO.ActionConfig action01 = new IotRuleSceneDO.ActionConfig(); + action01.setType(IotRuleSceneActionTypeEnum.DEVICE_CONTROL.getType()); + IotRuleSceneDO.ActionDeviceControl actionDeviceControl01 = new IotRuleSceneDO.ActionDeviceControl(); + actionDeviceControl01.setProductKey("4aymZgOTOOCrDKRT"); + actionDeviceControl01.setDeviceNames(ListUtil.of("small")); + actionDeviceControl01.setType(IotDeviceMessageTypeEnum.PROPERTY.getType()); + actionDeviceControl01.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier()); + actionDeviceControl01.setData(MapUtil.builder() + .put("power", 1) + .put("color", "red") + .build()); + action01.setDeviceControl(actionDeviceControl01); + scene.setActions(ListUtil.toList(action01)); + } + if (scene == null) { + log.error("[executeRuleSceneByTimer][规则场景({}) 不存在]", id); + return; + } + if (CommonStatusEnum.isDisable(scene.getStatus())) { + log.info("[executeRuleSceneByTimer][规则场景({}) 已被禁用]", id); + return; + } + // 1.2 判断是否有定时触发器,避免脏数据 + IotRuleSceneDO.TriggerConfig config = CollUtil.findOne(scene.getTriggers(), + trigger -> ObjUtil.equals(trigger.getType(), IotRuleSceneTriggerTypeEnum.TIMER.getType())); + if (config == null) { + log.error("[executeRuleSceneByTimer][规则场景({}) 不存在定时触发器]", scene); + return; + } + + // 2. 执行规则场景 + TenantUtils.execute(scene.getTenantId(), + () -> executeRuleSceneAction(null, ListUtil.toList(scene))); + } + /** - * 获得匹配的规则场景列表 + * 基于消息,获得匹配的规则场景列表 * * @param message 设备消息 * @return 规则场景列表 */ - private List getMatchedRuleSceneList(IotDeviceMessage message) { + private List getMatchedRuleSceneListByMessage(IotDeviceMessage message) { // 1. 匹配设备 // TODO @芋艿:可能需要 getSelf(); 缓存 List ruleScenes = getRuleSceneListByProductKeyAndDeviceNameFromCache( @@ -335,16 +389,37 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { }); } - // TODO @芋艿:测试思路代码,记得删除!!! - // 1. Job 类:IotRuleSceneJob - // 2. 参数:id - // 3. jobHandlerName:IotRuleSceneJob + id + @Override + @SneakyThrows + public void test() { + // TODO @芋艿:测试思路代码,记得删除!!! + // 1. Job 类:IotRuleSceneJob DONE + // 2. 参数:id DONE + // 3. jobHandlerName:IotRuleSceneJob + id DONE - // 新增:addJob - // 修改:不存在 addJob、存在 updateJob - // 有 + 禁用:1)存在、停止;2)不存在:不处理;TODO 测试:直接暂停,是否可以???(结论:可以) - // 有 + 开启:1)存在,更新;2)不存在,新增; - // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以) + // 新增:addJob + // 修改:不存在 addJob、存在 updateJob + // 有 + 禁用:1)存在、停止;2)不存在:不处理;TODO 测试:直接暂停,是否可以???(结论:可以)pauseJob + // 有 + 开启:1)存在,更新;2)不存在,新增;结论:使用 save(addOrUpdateJob) + // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob + + // + if (true) { + Long id = 1L; + Map jobDataMap = IotRuleSceneJob.buildJobDataMap(id); + schedulerManager.addOrUpdateJob(IotRuleSceneJob.class, + IotRuleSceneJob.buildJobName(id), + "0/10 * * * * ?", + jobDataMap); + } + if (false) { + Long id = 1L; + schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id)); + } + if (true) { + + } + } public static void main2(String[] args) throws SchedulerException { // System.out.println(QuartzJobBean.class); From d24e3ad773b62f656469fb919f634f2533900cc5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Feb 2025 13:41:52 +0800 Subject: [PATCH 151/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20alert=20?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E7=9B=B8=E5=85=B3=E7=9A=84=E8=A1=A8=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/IotAlertConfigReceiveTypeEnum.java | 31 ++++++++ .../thingmodel/IotDataSpecsDataTypeEnum.java | 1 + .../IotThingModelAccessModeEnum.java | 1 + .../IotThingModelParamDirectionEnum.java | 1 + .../IotThingModelServiceCallTypeEnum.java | 1 + .../IotThingModelServiceEventTypeEnum.java | 1 + .../dal/dataobject/rule/IotAlertConfig.java | 79 +++++++++++++++++++ .../dal/dataobject/rule/IotAlertRecordDO.java | 78 ++++++++++++++++++ .../rule/action/IotRuleSceneAction.java | 1 + .../rule/action/IotRuleSceneAlertAction.java | 28 +++++++ .../action/IotRuleSceneDataBridgeAction.java | 1 - 11 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java new file mode 100644 index 0000000000..87df89f763 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.iot.enums.rule; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * Iot 告警配置的接收方式枚举 + * + * @author 芋道源码 + */ +@RequiredArgsConstructor +@Getter +public enum IotAlertConfigReceiveTypeEnum implements ArrayValuable { + + SMS(1), // 短信 + MAIL(2), // 邮箱 + NOTIFY(3); // 通知 + + private final Integer type; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotAlertConfigReceiveTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java index 5133b00385..aa455f8cce 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; +// TODO @puhui999:加个 ArrayValuable /** * IoT 数据定义的数据类型枚举类 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java index 0b18bbbf5a..6eb6ddb7e5 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; +// TODO @puhui999:加个 ArrayValuable /** * IOT 产品物模型属性读取类型枚举 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java index b1ae8a9471..1e875cd234 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; +// TODO @puhui999:加个 ArrayValuable /** * IOT 产品物模型参数是输入参数还是输出参数枚举 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java index 66ca6bec57..d547bf1041 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; +// TODO @puhui999:加个 ArrayValuable /** * IOT 产品物模型服务调用方式枚举 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java index b87d7eb982..89271d7fca 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; import lombok.AllArgsConstructor; import lombok.Getter; +// TODO @puhui999:加个 ArrayValuable /** * IOT 产品物模型事件类型枚举 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java new file mode 100644 index 0000000000..94734a2d38 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotAlertConfigReceiveTypeEnum; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +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 lombok.*; + +import java.util.List; + +/** + * IoT 告警配置 DO + * + * @author 芋道源码 + */ +@TableName("iot_alert_config") +@KeySequence("iot_alert_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotAlertConfig extends BaseDO { + + /** + * 配置编号 + */ + @TableId + private Long id; + /** + * 配置名称 + */ + private String name; + /** + * 配置描述 + */ + private String description; + /** + * 配置状态 + * + * TODO 数据字典 + */ + private Integer level; + /** + * 配置状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + + /** + * 关联的规则场景编号数组 + * + * 关联 {@link IotRuleSceneDO#getId()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List ruleSceneIds; + + /** + * 接收的用户编号数组 + * + * 关联 {@link AdminUserRespDTO#getId()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List receiveUserIds; + /** + * 接收的类型数组 + * + * 枚举 {@link IotAlertConfigReceiveTypeEnum} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List receiveTypes; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java new file mode 100644 index 0000000000..2fc01ba7d3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.rule; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +/** + * IoT 告警记录 DO + * + * @author 芋道源码 + */ +@TableName("iot_alert_record") +@KeySequence("iot_alert_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotAlertRecordDO extends BaseDO { + + /** + * 记录编号 + */ + @TableField + private Long id; + /** + * 告警名称 + * + * 冗余 {@link IotAlertConfig#getName()} + */ + private Long configId; + /** + * 告警名称 + * + * 冗余 {@link IotAlertConfig#getName()} + */ + private String name; + + /** + * 产品标识 + * + * 关联 {@link IotProductDO#getProductKey()} ()} + */ + private String productKey; + /** + * 设备名称 + * + * 冗余 {@link IotDeviceDO#getDeviceName()} + */ + private String deviceName; + + // TODO @芋艿:有没更好的方式 + /** + * 触发的设备消息 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private IotDeviceMessage deviceMessage; + + /** + * 处理状态 + * + * true - 已处理 + * false - 未处理 + */ + private Boolean processStatus; + /** + * 处理结果(备注) + */ + private String processRemark; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java index 1cb17c9b44..4cf1f8f285 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java @@ -13,6 +13,7 @@ import javax.annotation.Nullable; */ public interface IotRuleSceneAction { + // TODO @芋艿:groovy 或者 javascript 实现数据的转换;可以考虑基于 hutool 的 ScriptUtil 做 /** * 执行场景 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java new file mode 100644 index 0000000000..eadc173787 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAlertAction.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.iot.service.rule.action; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import org.springframework.stereotype.Component; + +import javax.annotation.Nullable; + +/** + * IoT 告警的 {@link IotRuleSceneAction} 实现类 + * + * @author 芋道源码 + */ +@Component +public class IotRuleSceneAlertAction implements IotRuleSceneAction { + + @Override + public void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { + // TODO @芋艿:待实现 + } + + @Override + public IotRuleSceneActionTypeEnum getType() { + return IotRuleSceneActionTypeEnum.ALERT; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index f7f3747e78..82b0467140 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -60,7 +60,6 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { } // 2.1 执行 HTTP 请求 - // TODO @芋艿:groovy 或者 javascript 实现数据的转换;可以考虑基于 hutool 的 ScriptUtil 做 if (IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig()); return; From d23be8616483862d0cfe034fa532c9312ff2e3fb Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Feb 2025 16:35:55 +0800 Subject: [PATCH 152/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E7=89=A9=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingmodel/IotThingModelController.java | 7 ++-- .../module/iot/convert/package-info.java | 4 ++- .../thingmodel/IotThingModelConvert.java | 12 ------- .../thingmodel/IotThingModelServiceImpl.java | 35 +++++++++---------- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java index e4913486d3..382940fc48 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/IotThingModelController.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelL import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.thingmodel.IotThingModelConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; import io.swagger.v3.oas.annotations.Operation; @@ -62,7 +61,7 @@ public class IotThingModelController { @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") public CommonResult getThingModel(@RequestParam("id") Long id) { IotThingModelDO thingModel = thingModelService.getThingModel(id); - return success(IotThingModelConvert.INSTANCE.convert(thingModel)); + return success(BeanUtils.toBean(thingModel, IotThingModelRespVO.class)); } @GetMapping("/list-by-product-id") @@ -71,7 +70,7 @@ public class IotThingModelController { @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") public CommonResult> getThingModelListByProductId(@RequestParam("productId") Long productId) { List list = thingModelService.getThingModelListByProductId(productId); - return success(IotThingModelConvert.INSTANCE.convertList(list)); + return success(BeanUtils.toBean(list, IotThingModelRespVO.class)); } // TODO @puhui @super:getThingModelListByProductId 和 getThingModelListByProductId 可以融合么? @@ -80,7 +79,7 @@ public class IotThingModelController { @PreAuthorize("@ss.hasPermission('iot:thing-model:query')") public CommonResult> getThingModelListByProductId(@Valid IotThingModelListReqVO reqVO) { List list = thingModelService.getThingModelList(reqVO); - return success(IotThingModelConvert.INSTANCE.convertList(list)); + return success(BeanUtils.toBean(list, IotThingModelRespVO.class)); } @GetMapping("/page") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java index 28131f0905..18d7ad21db 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java @@ -1,4 +1,6 @@ /** - * TODO 芋艿:占位 + * 提供 POJO 类的实体转换 + * + * 目前使用 MapStruct 框架 */ package cn.iocoder.yudao.module.iot.convert; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java index 2e9b5441ce..9577b18f7b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/thingmodel/IotThingModelConvert.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.convert.thingmodel; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelEvent; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelProperty; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; -import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelRespVO; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; @@ -12,7 +11,6 @@ import org.mapstruct.Mapping; import org.mapstruct.Named; import org.mapstruct.factory.Mappers; -import java.util.List; import java.util.Objects; @Mapper @@ -20,21 +18,11 @@ public interface IotThingModelConvert { IotThingModelConvert INSTANCE = Mappers.getMapper(IotThingModelConvert.class); - // 将 SaveReqVO 转换为 DO @Mapping(target = "property", expression = "java(convertToProperty(bean))") @Mapping(target = "event", expression = "java(convertToEvent(bean))") @Mapping(target = "service", expression = "java(convertToService(bean))") IotThingModelDO convert(IotThingModelSaveReqVO bean); - // 将 DO 转换为 RespVO - @Mapping(target = "property", source = "property") - @Mapping(target = "event", source = "event") - @Mapping(target = "service", source = "service") - IotThingModelRespVO convert(IotThingModelDO bean); - - // 批量转换 - List convertList(List list); - @Named("convertToProperty") default ThingModelProperty convertToProperty(IotThingModelSaveReqVO bean) { if (Objects.equals(bean.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index 1b8cf79e0c..c57ab76ba7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -58,9 +58,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); // 1.2 功能名称在同一产品下是否唯一 validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); - // 1.3 系统保留字段,不能用于标识符定义 - validateNotDefaultEventAndService(createReqVO.getIdentifier()); - // 1.4 校验产品状态,发布状态下,不允许新增功能 + // 1.3 校验产品状态,发布状态下,不允许新增功能 validateProductStatus(createReqVO.getProductId()); // 2. 插入数据库 @@ -71,7 +69,6 @@ public class IotThingModelServiceImpl implements IotThingModelService { if (Objects.equals(createReqVO.getType(), IotThingModelTypeEnum.PROPERTY.getType())) { createDefaultEventsAndServices(createReqVO.getProductId(), createReqVO.getProductKey()); } - // TODO @puhui999: 服务和事件的情况 method 怎么设置?在前端设置还是后端设置? // 4. 删除缓存 deleteThingModelListCache(createReqVO.getProductKey()); @@ -177,14 +174,6 @@ public class IotThingModelServiceImpl implements IotThingModelService { } } - // TODO @芋艿:在 review 下 - private void validateNotDefaultEventAndService(String identifier) { - // 系统保留字段,不能用于标识符定义 - if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { - throw exception(THING_MODEL_IDENTIFIER_INVALID); - } - } - private void validateNameUnique(Long productId, String name) { IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndName(productId, name); if (thingModel != null) { @@ -193,6 +182,12 @@ public class IotThingModelServiceImpl implements IotThingModelService { } private void validateIdentifierUnique(Long productId, String identifier) { + // 系统保留字段,不能用于标识符定义 + if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { + throw exception(THING_MODEL_IDENTIFIER_INVALID); + } + + // 校验唯一 IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (thingModel != null) { throw exception(THING_MODEL_IDENTIFIER_EXISTS); @@ -215,17 +210,17 @@ public class IotThingModelServiceImpl implements IotThingModelService { // 2.1 生成属性上报事件 ThingModelEvent propertyPostEvent = generatePropertyPostEvent(properties); if (propertyPostEvent != null) { - newThingModels.add(buildEventThingModelDO(productId, productKey, propertyPostEvent, "属性上报事件")); + newThingModels.add(buildEventThingModel(productId, productKey, propertyPostEvent, "属性上报事件")); } // 2.2 生成属性设置服务 ThingModelService propertySetService = generatePropertySetService(properties); if (propertySetService != null) { - newThingModels.add(buildServiceThingModelDO(productId, productKey, propertySetService, "属性设置服务")); + newThingModels.add(buildServiceThingModel(productId, productKey, propertySetService, "属性设置服务")); } // 2.3 生成属性获取服务 ThingModelService propertyGetService = generatePropertyGetService(properties); if (propertyGetService != null) { - newThingModels.add(buildServiceThingModelDO(productId, productKey, propertyGetService, "属性获取服务")); + newThingModels.add(buildServiceThingModel(productId, productKey, propertyGetService, "属性获取服务")); } // 3.1 获取数据库中的默认的旧事件和服务列表 @@ -269,8 +264,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { /** * 构建事件功能对象 */ - private IotThingModelDO buildEventThingModelDO(Long productId, String productKey, - ThingModelEvent event, String description) { + private IotThingModelDO buildEventThingModel(Long productId, String productKey, + ThingModelEvent event, String description) { return new IotThingModelDO().setProductId(productId).setProductKey(productKey) .setIdentifier(event.getIdentifier()).setName(event.getName()).setDescription(description) .setType(IotThingModelTypeEnum.EVENT.getType()).setEvent(event); @@ -279,13 +274,14 @@ public class IotThingModelServiceImpl implements IotThingModelService { /** * 构建服务功能对象 */ - private IotThingModelDO buildServiceThingModelDO(Long productId, String productKey, - ThingModelService service, String description) { + private IotThingModelDO buildServiceThingModel(Long productId, String productKey, + ThingModelService service, String description) { return new IotThingModelDO().setProductId(productId).setProductKey(productKey) .setIdentifier(service.getIdentifier()).setName(service.getName()).setDescription(description) .setType(IotThingModelTypeEnum.SERVICE.getType()).setService(service); } + // TODO @haohao:是不是不用生成这个?目前属性上报,是个批量接口 /** * 生成属性上报事件 */ @@ -301,6 +297,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { .setOutputParams(buildInputOutputParam(thingModels, IotThingModelParamDirectionEnum.OUTPUT)); } + // TODO @haohao:是不是不用生成这个?目前属性上报,是个批量接口 /** * 生成属性设置服务 */ From b46e630912e6f4c11623b7341ff7a15de0aea0f5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Feb 2025 17:34:04 +0800 Subject: [PATCH 153/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9Aplugin=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/plugin/PluginInfoController.java | 1 + .../plugin/vo/info/PluginInfoSaveReqVO.java | 2 +- .../dataobject/plugin/IotPluginInfoDO.java | 6 ++- ...ation.java => IotPluginConfiguration.java} | 28 ++++++---- .../core/CustomPluginStateListener.java | 25 --------- .../plugin/core/IotPluginStartRunner.java | 52 +++++++++++++++++++ .../plugin/core/IotPluginStateListener.java | 21 ++++++++ .../framework/plugin/core/PluginStart.java | 49 ----------------- .../service/plugin/IotPluginInfoService.java | 2 +- .../plugin/IotPluginInfoServiceImpl.java | 40 ++++++-------- .../service/rule/IotRuleSceneServiceImpl.java | 5 +- 11 files changed, 120 insertions(+), 111 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/{UnifiedConfiguration.java => IotPluginConfiguration.java} (52%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java index 7d9472d249..9b78001595 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java @@ -78,6 +78,7 @@ public class PluginInfoController { return success(true); } + // TODO @haohao:要不独立一个 VO,不用 PluginInfoSaveReqVO @PutMapping("/update-status") @Operation(summary = "修改插件状态") @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java index e6453b4570..c2a16ac496 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java @@ -13,7 +13,7 @@ public class PluginInfoSaveReqVO { // TODO @haohao:一些枚举字段,需要加枚举校验。例如说,deployType、status、type 等 - @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") private Long id; @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java index f55a9f5ba4..3a9c588d4a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java @@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; +// TODO @haohao:建议 IotPluginInfoDO 改成 IotPluginConfigDO,插件配置。项目里,暂时没有用 info 作为配置的哈。 /** * IoT 插件信息 DO * @@ -38,7 +39,7 @@ public class IotPluginInfoDO extends BaseDO { */ private String name; /** - * 描述 + * 插件描述 */ private String description; /** @@ -68,12 +69,14 @@ public class IotPluginInfoDO extends BaseDO { */ // TODO @芋艿:枚举字段 private String protocol; + // TODO @haohao:这个字段,是不是直接用 CommonStatus,开启、禁用;然后插件实例那,online 是否在线 /** * 状态 *

* 枚举 {@link IotPluginStatusEnum} */ private Integer status; + // TODO @芋艿:configSchema、config 示例字段 /** * 插件配置项描述信息 @@ -83,6 +86,7 @@ public class IotPluginInfoDO extends BaseDO { * 插件配置信息 */ private String config; + // TODO @芋艿:script 后续的使用 /** * 插件脚本 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/UnifiedConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java similarity index 52% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/UnifiedConfiguration.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java index 849b6b16ff..b695cbe39f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/UnifiedConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.framework.plugin.config; -import cn.iocoder.yudao.module.iot.framework.plugin.core.CustomPluginStateListener; +import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStartRunner; +import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStateListener; +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService; import lombok.extern.slf4j.Slf4j; import org.pf4j.spring.SpringPluginManager; import org.springframework.beans.factory.annotation.Value; @@ -9,16 +11,24 @@ import org.springframework.context.annotation.Configuration; import java.nio.file.Paths; -// TODO @芋艿:需要 review 下 -@Slf4j +/** + * IoT 插件配置类 + * + * @author haohao + */ @Configuration -public class UnifiedConfiguration { - - @Value("${pf4j.pluginsDir:pluginsDir}") - private String pluginsDir; +@Slf4j +public class IotPluginConfiguration { @Bean - public SpringPluginManager pluginManager() { + public IotPluginStartRunner pluginStartRunner(SpringPluginManager pluginManager, + IotPluginInfoService pluginInfoService) { + return new IotPluginStartRunner(pluginManager, pluginInfoService); + } + + // TODO @芋艿:需要 review 下 + @Bean + public SpringPluginManager pluginManager(@Value("${pf4j.pluginsDir:pluginsDir}") String pluginsDir) { log.info("[init][实例化 SpringPluginManager]"); SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { // SpringPluginManager springPluginManager = new SpringPluginManager() { @@ -30,7 +40,7 @@ public class UnifiedConfiguration { } }; - springPluginManager.addPluginStateListener(new CustomPluginStateListener()); + springPluginManager.addPluginStateListener(new IotPluginStateListener()); return springPluginManager; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java deleted file mode 100644 index 9833a815b6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.framework.plugin.core; - -import lombok.extern.slf4j.Slf4j; -import org.pf4j.PluginStateEvent; -import org.pf4j.PluginStateListener; -import org.springframework.stereotype.Component; - -// TODO @芋艿:需要 review 下 -@Component -@Slf4j -public class CustomPluginStateListener implements PluginStateListener { - - @Override - public void pluginStateChanged(PluginStateEvent event) { - // 1. 获取插件ID - String pluginId = event.getPlugin().getPluginId(); - // 2. 获取插件旧状态 - String oldState = event.getOldState().toString(); - // 3. 获取插件新状态 - String newState = event.getPluginState().toString(); - // 4. 打印日志信息 - log.info("插件的状态 '{}' 已更改为 '{}' 至 '{}'", pluginId, oldState, newState); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java new file mode 100644 index 0000000000..f3654bc305 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.framework.plugin.core; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; + +import java.util.List; + +/** + * IoT 插件启动 Runner + * + * 用于 Spring Boot 启动时,启动 {@link IotPluginDeployTypeEnum#JAR} 部署类型的插件 + */ +@RequiredArgsConstructor +@Slf4j +public class IotPluginStartRunner implements ApplicationRunner { + + private final SpringPluginManager springPluginManager; + + private final IotPluginInfoService pluginInfoService; + + @Override + public void run(ApplicationArguments args) { + List pluginInfoList = TenantUtils.executeIgnore( + // TODO @haohao:需要查询部署类型哈 + () -> pluginInfoService.getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus())); + if (CollUtil.isEmpty(pluginInfoList)) { + log.info("[run][没有需要启动的插件]"); + return; + } + + // 遍历插件列表,逐个启动 + pluginInfoList.forEach(pluginInfo -> { + try { + log.info("[run][插件({}) 启动开始]", pluginInfo.getPluginKey()); + springPluginManager.startPlugin(pluginInfo.getPluginKey()); + log.info("[run][插件({}) 启动完成]", pluginInfo.getPluginKey()); + } catch (Exception e) { + log.error("[run][插件({}) 启动异常]", pluginInfo.getPluginKey(), e); + } + }); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java new file mode 100644 index 0000000000..bbc73c619e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.framework.plugin.core; + +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginStateEvent; +import org.pf4j.PluginStateListener; + +/** + * IoT 插件状态监听器,用于 log 插件的状态变化 + * + * @author haohao + */ +@Slf4j +public class IotPluginStateListener implements PluginStateListener { + + @Override + public void pluginStateChanged(PluginStateEvent event) { + log.info("[pluginStateChanged][插件({}) 状态变化,从 {} 变为 {}]", event.getPlugin().getPluginId(), + event.getOldState().toString(), event.getPluginState().toString()); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java deleted file mode 100644 index 76b0fa67bd..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.iot.framework.plugin.core; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService; -import lombok.extern.slf4j.Slf4j; -import org.pf4j.spring.SpringPluginManager; -import org.springframework.boot.ApplicationArguments; -import org.springframework.boot.ApplicationRunner; -import org.springframework.stereotype.Component; - -import javax.annotation.Resource; -import java.util.List; - -// TODO @芋艿:需要 review 下 -@Component -@Slf4j -public class PluginStart implements ApplicationRunner { - - @Resource - private IotPluginInfoService pluginInfoService; - - @Resource - private SpringPluginManager pluginManager; - - @Override - public void run(ApplicationArguments args) { - TenantUtils.executeIgnore(() -> { // 1. 忽略租户上下文执行 - List pluginInfoList = pluginInfoService - .getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); // 2. 获取运行中的插件列表 - if (CollUtil.isEmpty(pluginInfoList)) { // 3. 检查插件列表是否为空 - log.info("[run] 没有需要启动的插件"); // 4. 日志记录没有插件需要启动 - return; - } - pluginInfoList.forEach(pluginInfo -> { // 5. 使用lambda表达式遍历插件列表 - try { - log.info("[run][启动插件] pluginKey = {}", pluginInfo.getPluginKey()); // 6. 日志记录插件启动信息 - pluginManager.startPlugin(pluginInfo.getPluginKey()); // 7. 启动插件 - } catch (Exception e) { - log.error("[run][启动插件失败] pluginKey = {}", pluginInfo.getPluginKey(), e); // 8. 记录启动失败的日志 - } - }); - }); - - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java index 3ac8680b8d..1a8654ebc6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java @@ -14,7 +14,7 @@ import java.util.List; /** * IoT 插件信息 Service 接口 * - * @author 芋道源码 + * @author haohao */ public interface IotPluginInfoService { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java index c111da3267..400f1e39ea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java @@ -23,7 +23,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_N /** * IoT 插件信息 Service 实现类 * - * @author 芋道源码 + * @author haohao */ @Service @Validated @@ -37,7 +37,7 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService { private IotPluginInstanceService pluginInstanceService; @Resource - private SpringPluginManager pluginManager; + private SpringPluginManager springPluginManager; @Override public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { @@ -60,18 +60,17 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService { public void deletePluginInfo(Long id) { // 1.1 校验存在 IotPluginInfoDO pluginInfoDO = validatePluginInfoExists(id); - // 1.2 停止插件 + // 1.2 未开启状态,才允许删除 if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING); } - // 2. 卸载插件 + // 2.1 卸载插件 pluginInstanceService.stopAndUnloadPlugin(pluginInfoDO.getPluginKey()); - - // 3. 删除插件文件 + // 2.2 删除插件文件 pluginInstanceService.deletePluginFile(pluginInfoDO); - // 4. 删除插件信息 + // 3. 删除插件信息 pluginInfoMapper.deleteById(id); } @@ -97,17 +96,18 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService { public void uploadFile(Long id, MultipartFile file) { // 1. 校验插件信息是否存在 IotPluginInfoDO pluginInfoDo = validatePluginInfoExists(id); + // TODO @haohao:最好校验下 file 相关参数,是否完整,类似:version 之类是不是可以解析到 - // 2. 停止并卸载旧的插件 + // 2.1 停止并卸载旧的插件 pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); - - // 3 上传新的插件文件,更新插件启用状态文件 + // 2.2 上传新的插件文件,更新插件启用状态文件 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); - // 4. 更新插件信息 + // 3. 更新插件信息 updatePluginInfo(pluginInfoDo, pluginKeyNew, file); } + // TODO @haohao:这个方法,要不合并到 uploadFile 里; /** * 更新插件信息 * @@ -116,18 +116,15 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService { * @param file 文件 */ private void updatePluginInfo(IotPluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) { - // 创建新的插件信息对象并链式设置属性 IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO() .setId(pluginInfoDo.getId()) .setPluginKey(pluginKeyNew) - .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) + .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈? .setFileName(file.getOriginalFilename()) - .setScript("") - .setConfigSchema(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) - .setVersion(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) - .setDescription(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); - - // 执行更新 + .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来? + .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) + .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) + .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); pluginInfoMapper.updateById(updatedPluginInfo); } @@ -140,10 +137,7 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService { pluginInstanceService.updatePluginStatus(pluginInfoDo, status); // 3. 更新数据库中的插件状态 - IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO(); - updatedPluginInfo.setId(id); - updatedPluginInfo.setStatus(status); - pluginInfoMapper.updateById(updatedPluginInfo); + pluginInfoMapper.updateById(new IotPluginInfoDO().setId(id).setStatus(status)); } @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java index 7f8ff51f10..2219d4bad1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java @@ -404,7 +404,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob // - if (true) { + if (false) { Long id = 1L; Map jobDataMap = IotRuleSceneJob.buildJobDataMap(id); schedulerManager.addOrUpdateJob(IotRuleSceneJob.class, @@ -417,7 +417,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService { schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id)); } if (true) { - + Long id = 1L; + schedulerManager.deleteJob(IotRuleSceneJob.buildJobName(id)); } } From 4e43958fe3000b2cda46ff34b28fb010eadd806e Mon Sep 17 00:00:00 2001 From: mcwindy Date: Wed, 5 Feb 2025 11:42:59 +0800 Subject: [PATCH 154/309] fix typo: adminTenentId -> adminTenantId, appTenentId -> appTenantId --- script/idea/http-client.env.json | 6 ++--- .../admin/chat/AiChatMessageController.http | 4 ++-- .../permission/CrmPermissionController.http | 6 ++--- .../CrmStatisticsCustomerController.http | 24 +++++++++---------- .../CrmStatisticsRankController.http | 4 ++-- .../ErpSaleStatisticsController.http | 4 ++-- .../admin/file/FileConfigController.http | 6 ++--- .../controller/admin/job/JobController.http | 2 +- .../admin/redis/RedisController.http | 2 +- .../IotThinkModelFunctionController.http | 12 +++++----- .../admin/spu/ProductSpuController.http | 2 +- .../app/spu/AppProductSpuController.http | 8 +++---- .../app/activity/AppActivityController.http | 2 +- .../app/bargain/AppBargainHelpController.http | 2 +- .../bargain/AppBargainRecordController.http | 2 +- .../aftersale/TradeAfterSaleController.http | 10 ++++---- .../admin/order/TradeOrderController.http | 6 ++--- .../app/cart/AppCartController.http | 12 +++++----- .../app/order/AppTradeOrderController.http | 16 ++++++------- .../app/address/AppAddressController.http | 12 +++++----- .../app/auth/AppAuthController.http | 16 ++++++------- .../app/user/AppMemberUserController.http | 2 +- .../admin/material/MpMaterialController.http | 2 +- .../admin/menu/MpMenuController.http | 6 ++--- .../admin/message/MpAutoReplyController.http | 2 +- .../admin/message/MpMessageController.http | 6 ++--- .../admin/news/MpDraftController.http | 6 ++--- .../admin/news/MpFreePublishController.http | 4 ++-- .../controller/admin/tag/MpTagController.http | 10 ++++---- .../admin/user/MpUserController.http | 4 ++-- .../app/order/AppPayOrderController.http | 10 ++++---- .../controller/admin/auth/AuthController.http | 8 +++---- .../admin/dict/DictDataController.http | 2 +- .../controller/admin/ip/AreaController.http | 2 +- .../admin/logger/OperateLogController.http | 2 +- .../admin/mail/MailTemplateController.http | 2 +- .../admin/oauth2/OAuth2ClientController.http | 2 +- .../admin/oauth2/OAuth2OpenController.http | 16 ++++++------- .../admin/oauth2/OAuth2UserController.http | 4 ++-- .../admin/permission/MenuController.http | 2 +- .../admin/permission/RoleController.http | 10 ++++---- .../admin/sms/SmsTemplateController.http | 2 +- .../admin/socail/SocialClientController.http | 2 +- .../admin/tenant/TenantController.http | 2 +- .../controller/admin/user/UserController.http | 2 +- .../admin/user/UserProfileController.http | 2 +- 46 files changed, 135 insertions(+), 135 deletions(-) diff --git a/script/idea/http-client.env.json b/script/idea/http-client.env.json index 4a4cb5221e..e6e8a7cf86 100644 --- a/script/idea/http-client.env.json +++ b/script/idea/http-client.env.json @@ -2,16 +2,16 @@ "local": { "baseUrl": "http://127.0.0.1:48080/admin-api", "token": "test1", - "adminTenentId": "1", + "adminTenantId": "1", "appApi": "http://127.0.0.1:48080/app-api", "appToken": "test247", - "appTenentId": "1" + "appTenantId": "1" }, "gateway": { "baseUrl": "http://127.0.0.1:8888/admin-api", "token": "test1", - "adminTenentId": "1", + "adminTenantId": "1", "appApi": "http://127.0.0.1:8888/app-api", "appToken": "test1", diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http index e75e0d3335..4c4c8c0891 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/ai/chat/message/send Content-Type: application/json Authorization: {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "conversationId": "1781604279872581724", @@ -13,7 +13,7 @@ tenant-id: {{adminTenentId}} POST {{baseUrl}}/ai/chat/message/send-stream Content-Type: application/json Authorization: {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "conversationId": "1781604279872581724", diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http index 1ef2bc1a1d..7abc26705c 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/permission/CrmPermissionController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/crm/permission/create Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "userId": 1, @@ -15,7 +15,7 @@ tenant-id: {{adminTenentId}} PUT {{baseUrl}}/crm/permission/update Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "userId": 1, @@ -28,5 +28,5 @@ tenant-id: {{adminTenentId}} ### 请求 /delete DELETE {{baseUrl}}/crm/permission/delete?bizType=2&bizId=1&id=1 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http index 6b960512d8..4f343165e3 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.http @@ -2,64 +2,64 @@ ### 1.1 客户总量分析(按日期) GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 1.2 客户总量统计(按用户) GET {{baseUrl}}/crm/statistics-customer/get-customer-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} # == 2. 客户跟进次数分析 == ### 2.1 客户跟进次数分析(按日期) GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 2.2 客户总量统计(按用户) GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} # == 3. 客户跟进方式分析 == ### 3.1 客户跟进方式分析 GET {{baseUrl}}/crm/statistics-customer/get-follow-up-summary-by-type?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} # == 4. 客户成交周期 == ### 4.1 合同摘要信息(客户转化率页面) GET {{baseUrl}}/crm/statistics-customer/get-contract-summary?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} # == 5. 客户成交周期 == ### 5.1 获取客户公海分析(按日期) GET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-date?deptId=100&interval=2×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 5.2 获取客户公海分析(按用户) GET {{baseUrl}}/crm/statistics-customer/get-pool-summary-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} # == 6. 客户成交周期 == ### 6.1 客户成交周期(按日期) GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-date?deptId=100&interval=2×[0]=2024-01-01 00:00:00×[1]=2024-01-29 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 6.2 获取客户成交周期(按用户) GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-user?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 6.3 获取客户成交周期(按区域) GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-area?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 6.4 获取客户成交周期(按产品) GET {{baseUrl}}/crm/statistics-customer/get-customer-deal-cycle-by-product?deptId=100×[0]=2023-01-01 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.http b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.http index e878ba1a93..407562ddbc 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.http +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.http @@ -1,9 +1,9 @@ ### 合同金额排行榜 GET {{baseUrl}}/crm/statistics-rank/get-contract-price-rank?deptId=100×[0]=2022-12-12 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 回款金额排行榜 GET {{baseUrl}}/crm/statistics-rank/get-receivable-price-rank?deptId=100×[0]=2022-12-12 00:00:00×[1]=2024-12-12 23:59:59 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} \ No newline at end of file +tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http index 5f5cab1094..0dca8935c5 100644 --- a/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http +++ b/yudao-module-erp/yudao-module-erp-biz/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpSaleStatisticsController.http @@ -1,11 +1,11 @@ ### 请求 /erp/sale-statistics/summary 接口 => 成功 GET {{baseUrl}}/erp/sale-statistics/summary Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} ### 请求 /erp/sale-statistics/time-summary 接口 => 成功 GET {{baseUrl}}/erp/sale-statistics/time-summary Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http index 499f64df7d..14b6228c94 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http @@ -1,7 +1,7 @@ ### 请求 /infra/file-config/create 接口 => 成功 POST {{baseUrl}}/infra/file-config/create Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} { @@ -21,7 +21,7 @@ Authorization: Bearer {{token}} ### 请求 /infra/file-config/update 接口 => 成功 PUT {{baseUrl}}/infra/file-config/update Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} { @@ -41,5 +41,5 @@ Authorization: Bearer {{token}} ### 请求 /infra/file-config/test 接口 => 成功 GET {{baseUrl}}/infra/file-config/test?id=2 Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http index 62f8dd6066..52e7394c5a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http @@ -1,5 +1,5 @@ ### 请求 /infra/job/sync 接口 => 成功 POST {{baseUrl}}/infra/job/sync Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http index 8a0e70fd34..68b5487e27 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http @@ -1,4 +1,4 @@ ### 请求 /infra/redis/get-monitor-info 接口 => 成功 GET {{baseUrl}}/infra/redis/get-monitor-info Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http index 56464dd80b..84446b0ced 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http @@ -1,7 +1,7 @@ ### 请求 /iot/think-model-function/create 接口 => 成功 POST {{baseUrl}}/iot/think-model-function/create Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} { @@ -32,7 +32,7 @@ Authorization: Bearer {{token}} ### 请求 /iot/think-model-function/create 接口 => 成功 POST {{baseUrl}}/iot/think-model-function/create Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} { @@ -66,7 +66,7 @@ Authorization: Bearer {{token}} ### 请求 /iot/think-model-function/update 接口 => 成功 PUT {{baseUrl}}/iot/think-model-function/update Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} { @@ -97,16 +97,16 @@ Authorization: Bearer {{token}} ### 请求 /iot/think-model-function/delete 接口 => 成功 DELETE {{baseUrl}}/iot/think-model-function/delete?id=7 -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} ### 请求 /iot/think-model-function/get 接口 => 成功 GET {{baseUrl}}/iot/think-model-function/get?id=10 -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} ### 请求 /iot/think-model-function/list-by-product-id 接口 => 成功 GET {{baseUrl}}/iot/think-model-function/list-by-product-id?productId=1001 -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Authorization: Bearer {{token}} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http index 4ab7b4f714..8b20673995 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.http @@ -1,4 +1,4 @@ ### 获得商品 SPU 明细 GET {{baseUrl}}/product/spu/get-detail?id=4 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http index c391b5873d..784c464608 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.http @@ -1,18 +1,18 @@ ### 获得订单交易的分页(默认) GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 获得订单交易的分页(价格) GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=price&sortAsc=true Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 获得订单交易的分页(销售) GET {{appApi}}/product/spu/page?pageNo=1&pageSize=10&sortField=salesCount&sortAsc=true Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 获得商品 SPU 明细 GET {{appApi}}/product/spu/get-detail?id=102 -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http index 0dda88c7d8..e74a46a11e 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http @@ -2,4 +2,4 @@ GET {{appApi}}/promotion/activity/list-by-spu-ids?spuIds=222&spuIds=633 Authorization: Bearer {{appToken}} Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http index 2e401e9d62..a3b3d225fc 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainHelpController.http @@ -2,7 +2,7 @@ POST {{appApi}}/promotion/bargain-help/create Authorization: Bearer test248 Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "recordId": 26 diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.http b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.http index 46cbe3c8ec..dde40f8078 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.http +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.http @@ -2,7 +2,7 @@ POST {{appApi}}/promotion/bargain-record/create Authorization: Bearer {{appToken}} Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "activityId": 1 diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.http index 81cb35cbf0..f342e948a3 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.http +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/aftersale/TradeAfterSaleController.http @@ -1,18 +1,18 @@ ### 获得交易售后分页 => 成功 GET {{baseUrl}}/trade/after-sale/page?pageNo=1&pageSize=10 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 同意售后 => 成功 PUT {{baseUrl}}/trade/after-sale/agree?id=7 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Content-Type: application/json ### 拒绝售后 => 成功 PUT {{baseUrl}}/trade/after-sale/disagree Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Content-Type: application/json { @@ -23,11 +23,11 @@ Content-Type: application/json ### 确认退款 => 成功 PUT {{baseUrl}}/trade/after-sale/refund?id=6 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Content-Type: application/json ### 确认收货 => 成功 PUT {{baseUrl}}/trade/after-sale/receive?id=7 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} Content-Type: application/json diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http index 7877faadef..5255d1b8f6 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/order/TradeOrderController.http @@ -1,14 +1,14 @@ ### 获得交易订单分页 => 成功 GET {{baseUrl}}/trade/order/page?pageNo=1&pageSize=10 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 获得交易订单分页 => 成功 GET {{baseUrl}}/trade/order/get-detail?id=21 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 获得交易订单的物流轨迹 => 成功 GET {{baseUrl}}/trade/order/get-express-track-list?id=21 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} \ No newline at end of file +tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http index b341a48866..2eda3c2c36 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/cart/AppCartController.http @@ -1,6 +1,6 @@ ### 请求 /trade/cart/add 接口 => 成功 POST {{appApi}}/trade/cart/add -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} Content-Type: application/json @@ -12,7 +12,7 @@ Content-Type: application/json ### 请求 /trade/cart/update 接口 => 成功 PUT {{appApi}}/trade/cart/update -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} Content-Type: application/json @@ -23,20 +23,20 @@ Content-Type: application/json ### 请求 /trade/cart/delete 接口 => 成功 DELETE {{appApi}}/trade/cart/delete?ids=1 -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} ### 请求 /trade/cart/get-count 接口 => 成功 GET {{appApi}}/trade/cart/get-count -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} ### 请求 /trade/cart/get-count-map 接口 => 成功 GET {{appApi}}/trade/cart/get-count-map -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} ### 请求 /trade/cart/list 接口 => 成功 GET {{appApi}}/trade/cart/list -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http index 59490a7736..f51ddff005 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http @@ -1,18 +1,18 @@ ### /trade-order/settlement 获得订单结算信息(基于商品) GET {{appApi}}/trade/order/settlement?type=0&items[0].skuId=1&items[0].count=2&items[1].skuId=2&items[1].count=3&couponId=1 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### /trade-order/settlement 获得订单结算信息(基于购物车) GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### /trade-order/create 创建订单(基于商品)【快递】 POST {{appApi}}/trade/order/create Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "pointStatus": true, @@ -31,7 +31,7 @@ tenant-id: {{appTenentId}} POST {{appApi}}/trade/order/create Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "pointStatus": true, @@ -51,19 +51,19 @@ tenant-id: {{appTenentId}} ### 获得订单交易的分页 GET {{appApi}}/trade/order/page?pageNo=1&pageSize=10 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 获得订单交易的详细 GET {{appApi}}/trade/order/get-detail?id=21 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 获得交易订单的物流轨迹 GET {{appApi}}/trade/order/get-express-track-list?id=70 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### /trade-order/settlement-product 获得商品结算信息 GET {{appApi}}/trade/order/settlement-product?spuIds=633 Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} \ No newline at end of file +tenant-id: {{appTenantId}} \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http index 6bae7c7ebe..a0582e6d3b 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/address/AppAddressController.http @@ -1,7 +1,7 @@ ### 请求 /create 接口 => 成功 POST {{appApi}}//member/address/create Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} { @@ -16,7 +16,7 @@ Authorization: Bearer {{appToken}} ### 请求 /update 接口 => 成功 PUT {{appApi}}//member/address/update Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} { @@ -32,23 +32,23 @@ Authorization: Bearer {{appToken}} ### 请求 /delete 接口 => 成功 DELETE {{appApi}}//member/address/delete?id=2 Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} ### 请求 /get 接口 => 成功 GET {{appApi}}//member/address/get?id=1 Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} ### 请求 /get-default 接口 => 成功 GET {{appApi}}//member/address/get-default Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} ### 请求 /list 接口 => 成功 GET {{appApi}}//member/address/list Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} Authorization: Bearer {{appToken}} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http index 5b68d69845..391aa922c4 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http @@ -1,7 +1,7 @@ ### 请求 /login 接口 => 成功 POST {{appApi}}/member/auth/login Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "mobile": "15601691388", @@ -11,7 +11,7 @@ tenant-id: {{appTenentId}} ### 请求 /send-sms-code 接口 => 成功 POST {{appApi}}/member/auth/send-sms-code Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "mobile": "15601691388", @@ -21,7 +21,7 @@ tenant-id: {{appTenentId}} ### 请求 /sms-login 接口 => 成功 POST {{appApi}}/member/auth/sms-login Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} terminal: 30 { @@ -32,7 +32,7 @@ terminal: 30 ### 请求 /social-login 接口 => 成功 POST {{appApi}}/member/auth/social-login Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "type": 34, @@ -43,7 +43,7 @@ tenant-id: {{appTenentId}} ### 请求 /weixin-mini-app-login 接口 => 成功 POST {{appApi}}/member/auth/weixin-mini-app-login Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "phoneCode": "618e6412e0c728f5b8fc7164497463d0158a923c9e7fd86af8bba393b9decbc5", @@ -54,14 +54,14 @@ tenant-id: {{appTenentId}} POST {{appApi}}/member/auth/logout Content-Type: application/json Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66 -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 请求 /auth/refresh-token 接口 => 成功 POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70 Content-Type: application/json -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} ### 请求 /auth/create-weixin-jsapi-signature 接口 => 成功 POST {{appApi}}/member/auth/create-weixin-jsapi-signature?url=http://www.iocoder.cn Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http index 745556f75a..8ffb70caca 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/AppMemberUserController.http @@ -1,4 +1,4 @@ ### 请求 /member/user/profile/get 接口 => 没有权限 GET {{appApi}}/member/user/get Authorization: Bearer test245 -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http index 74b8f40b68..c093adf71c 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/material/MpMaterialController.http @@ -2,4 +2,4 @@ GET {{baseUrl}}/mp/material/page?permanent=true&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http index 2276b3b4f9..defd7ecb5f 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/menu/MpMenuController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/mp/menu/save Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "accountId": "1", @@ -37,7 +37,7 @@ tenant-id: {{adminTenentId}} POST {{baseUrl}}/mp/menu/save Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "accountId": "1", @@ -47,4 +47,4 @@ tenant-id: {{adminTenentId}} ### 请求 /mp/menu/list 接口 => 成功 GET {{baseUrl}}/mp/menu/list?accountId=1 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.http index dbb3a7b22b..8d17c6bd52 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpAutoReplyController.http @@ -2,4 +2,4 @@ GET {{baseUrl}}/mp/auto-reply/page?accountId=1&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http index b9f9721c9b..16677d4301 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/message/MpMessageController.http @@ -2,13 +2,13 @@ GET {{baseUrl}}/mp/message/page?accountId=1&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /mp/message/send 接口 => 成功(文本) POST {{baseUrl}}/mp/message/send Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "userId": 3, @@ -20,7 +20,7 @@ tenant-id: {{adminTenentId}} POST {{baseUrl}}/mp/message/send Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "userId": 3, diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpDraftController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpDraftController.http index 87f9d432a1..ea754292c3 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpDraftController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpDraftController.http @@ -2,13 +2,13 @@ GET {{baseUrl}}/mp/draft/page?accountId=1&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /mp/draft/create 接口 => 成功 POST {{baseUrl}}/mp/draft/create?accountId=1 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "articles": [ @@ -35,7 +35,7 @@ tenant-id: {{adminTenentId}} PUT {{baseUrl}}/mp/draft/update?accountId=1&mediaId=r6ryvl6LrxBU0miaST4Y-q-G9pdsmZw0OYG4FzHQkKfpLfEwIH51wy2bxisx8PvW Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} [{ "title": "我是标题(OOO)", diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpFreePublishController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpFreePublishController.http index 122413200e..246a95c4a0 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpFreePublishController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/news/MpFreePublishController.http @@ -2,12 +2,12 @@ GET {{baseUrl}}/mp/free-publish/page?accountId=1&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /mp/free-publish/submit 接口 => 成功 POST {{baseUrl}}/mp/free-publish/submit?accountId=1&mediaId=r6ryvl6LrxBU0miaST4Y-vilmd7iS51D8IPddxflWrau0hIQ2ovY8YanO5jlgUcM Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} {} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http index fe79105ba7..a888145e1f 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/tag/MpTagController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/mp/tag/create Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "accountId": "1", @@ -13,7 +13,7 @@ tenant-id: {{adminTenentId}} PUT {{baseUrl}}/mp/tag/update Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "id": "3", @@ -24,16 +24,16 @@ tenant-id: {{adminTenentId}} DELETE {{baseUrl}}/mp/tag/delete?id=3 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /mp/tag/page 接口 => 成功 GET {{baseUrl}}/mp/tag/page?accountId=1&pageNo=1&pageSize=10 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /mp/tag/sync 接口 => 成功 POST {{baseUrl}}/mp/tag/sync?accountId=1 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/MpUserController.http b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/MpUserController.http index 7c615810fc..c78356aad1 100644 --- a/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/MpUserController.http +++ b/yudao-module-mp/yudao-module-mp-biz/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/user/MpUserController.http @@ -2,13 +2,13 @@ POST {{baseUrl}}/mp/user/sync?accountId=1 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /mp/user/update 接口 => 成功 PUT {{baseUrl}}/mp/user/update Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "id": "3", diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http index 14ce54ef92..4fe1898b4d 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http @@ -2,7 +2,7 @@ POST {{appApi}}/pay/order/submit Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "id": 174, @@ -13,7 +13,7 @@ tenant-id: {{appTenentId}} POST {{appApi}}/pay/order/submit Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "id": 202, @@ -27,7 +27,7 @@ tenant-id: {{appTenentId}} POST {{appApi}}/pay/order/submit Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "id": 202, @@ -41,7 +41,7 @@ tenant-id: {{appTenentId}} POST {{appApi}}/pay/order/submit Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "id": 202, @@ -55,7 +55,7 @@ tenant-id: {{appTenentId}} POST {{appApi}}/pay/order/submit Content-Type: application/json Authorization: Bearer {{appToken}} -tenant-id: {{appTenentId}} +tenant-id: {{appTenantId}} { "id": 202, diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http index 00ae2ba2b3..f42dfcd030 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http @@ -1,7 +1,7 @@ ### 请求 /login 接口 => 成功 POST {{baseUrl}}/system/auth/login Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} tag: Yunai.local { @@ -14,7 +14,7 @@ tag: Yunai.local ### 请求 /login 接口 => 成功(无验证码) POST {{baseUrl}}/system/auth/login Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "username": "admin", @@ -24,10 +24,10 @@ tenant-id: {{adminTenentId}} ### 请求 /get-permission-info 接口 => 成功 GET {{baseUrl}}/system/auth/get-permission-info Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /list-menus 接口 => 成功 GET {{baseUrl}}/system/list-menus Authorization: Bearer {{token}} #Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http index f524315026..5a7ce8ee15 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http @@ -1,4 +1,4 @@ ### 请求 /menu/list 接口 => 成功 GET {{baseUrl}}/system/dict-data/list-all-simple Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http index f1b893d016..14165619ef 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http @@ -1,5 +1,5 @@ ### 获得地区树 GET {{baseUrl}}/system/area/tree Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http index 4853581656..be3102eb6f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http @@ -1,4 +1,4 @@ ### 请求 /system/operate-log/page 接口 => 成功 GET {{baseUrl}}/system/operate-log/page Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http index f3c47f514d..9ad2ed2085 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/system/mail-template/send-mail Authorization: Bearer {{token}} Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "templateCode": "test_01", diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http index dcf60a6cf2..f8d7b8931d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/system/oauth2-client/create Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "id": "1", diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http index 725a5d4f59..f770ed91d7 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http @@ -1,13 +1,13 @@ ### 请求 /system/oauth2/authorize 接口 => 成功 GET {{baseUrl}}/system/oauth2/authorize?clientId=default Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /system/oauth2/authorize + token 接口 => 成功 POST {{baseUrl}}/system/oauth2/authorize Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true @@ -15,7 +15,7 @@ response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=htt POST {{baseUrl}}/system/oauth2/authorize Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false @@ -23,7 +23,7 @@ response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=http POST {{baseUrl}}/system/oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a @@ -31,7 +31,7 @@ grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07 POST {{baseUrl}}/system/oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} grant_type=password&username=admin&password=admin123&scope=user.read @@ -39,16 +39,16 @@ grant_type=password&username=admin&password=admin123&scope=user.read POST {{baseUrl}}/system/oauth2/token Content-Type: application/x-www-form-urlencoded Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588 ### 请求 /system/oauth2/token + DELETE 接口 => 成功 DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596 Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /system/oauth2/check-token 接口 => 成功 POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106 Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http index 13c8545bda..9e76c7cdcc 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http @@ -1,13 +1,13 @@ ### 请求 /system/oauth2/user/get 接口 => 成功 GET {{baseUrl}}/system/oauth2/user/get Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### 请求 /system/oauth2/user/update 接口 => 成功 PUT {{baseUrl}}/system/oauth2/user/update Content-Type: application/json Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "nickname": "芋道源码" diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http index a90d8b8ab3..fbaff1e509 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http @@ -1,4 +1,4 @@ ### 请求 /menu/list 接口 => 成功 GET {{baseUrl}}/system/menu/list Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http index c68b86b759..375180a344 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/system/role/create Authorization: Bearer {{token}} Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "name": "测试角色", @@ -14,7 +14,7 @@ tenant-id: {{adminTenentId}} POST {{baseUrl}}/system/role/update Authorization: Bearer {{token}} Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "id": 100, @@ -26,7 +26,7 @@ tenant-id: {{adminTenentId}} POST {{baseUrl}}/system/role/delete Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} roleId=14 @@ -34,9 +34,9 @@ roleId=14 GET {{baseUrl}}/system/role/get?id=100 Content-Type: application/x-www-form-urlencoded Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} ### /role/page 成功 GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10 Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http index ee24e928be..3b975c39dd 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http @@ -2,7 +2,7 @@ POST {{baseUrl}}/system/sms-template/send-sms Authorization: Bearer {{token}} Content-Type: application/json -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "templateCode": "test_01", diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http index 5ab64392a7..9909b519ad 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http @@ -3,7 +3,7 @@ POST {{baseUrl}}/system/social-client/send-subscribe-message Authorization: Bearer {{token}} Content-Type: application/json #Authorization: Bearer test100 -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "userId": 247, diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http index a4d517385b..38aa594eaf 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http @@ -5,7 +5,7 @@ GET {{baseUrl}}/system/tenant/get-id-by-name?name=芋道源码 POST {{baseUrl}}/system/tenant/create Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} { "name": "芋道", diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http index 7d7f6227e8..90f0c98324 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http @@ -2,4 +2,4 @@ GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 Authorization: Bearer {{token}} #Authorization: Bearer test100 -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http index f06037b37b..c94c2ad23b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http @@ -1,4 +1,4 @@ ### 请求 /system/user/profile/get 接口 => 没有权限 GET {{baseUrl}}/system/user/profile/get Authorization: Bearer {{token}} -tenant-id: {{adminTenentId}} +tenant-id: {{adminTenantId}} From 6eadbba34554f2364f7bb77daed829bec8945637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 5 Feb 2025 21:44:23 +0800 Subject: [PATCH 155/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91IoT=EF=BC=9A=E9=87=8D=E6=9E=84=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86=EF=BC=8C=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=20PluginInfo=20=E4=B8=BA=20PluginConfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../yudao-spring-boot-starter-mybatis/pom.xml | 4 + .../module/iot/enums/ErrorCodeConstants.java | 7 +- yudao-module-iot/yudao-module-iot-biz/pom.xml | 5 - .../admin/plugin/PluginConfigController.java | 90 +++++++++ .../admin/plugin/PluginInfoController.java | 90 --------- .../PluginConfigImportReqVO.java} | 4 +- .../PluginConfigPageReqVO.java} | 6 +- .../PluginConfigRespVO.java} | 6 +- .../PluginConfigSaveReqVO.java} | 6 +- .../vo/config/PluginConfigStatusReqVO.java | 19 ++ ...uginInfoDO.java => IotPluginConfigDO.java} | 12 +- .../plugin/IotPluginInstanceDO.java | 2 +- .../mysql/plugin/IotPluginConfigMapper.java | 33 +++ .../dal/mysql/plugin/IotPluginInfoMapper.java | 32 --- .../plugin/config/IotPluginConfiguration.java | 7 +- .../plugin/core/IotPluginStartRunner.java | 27 +-- .../plugin/IotPluginConfigService.java | 100 ++++++++++ .../plugin/IotPluginConfigServiceImpl.java | 188 ++++++++++++++++++ .../service/plugin/IotPluginInfoService.java | 92 --------- .../plugin/IotPluginInfoServiceImpl.java | 158 --------------- .../plugin/IotPluginInstanceService.java | 10 +- .../plugin/IotPluginInstanceServiceImpl.java | 20 +- yudao-server/pom.xml | 10 +- 24 files changed, 494 insertions(+), 436 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/{info/PluginInfoImportReqVO.java => config/PluginConfigImportReqVO.java} (83%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/{info/PluginInfoPageReqVO.java => config/PluginConfigPageReqVO.java} (71%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/{info/PluginInfoRespVO.java => config/PluginConfigRespVO.java} (90%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/{info/PluginInfoSaveReqVO.java => config/PluginConfigSaveReqVO.java} (91%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/{IotPluginInfoDO.java => IotPluginConfigDO.java} (82%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginConfigMapper.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInfoMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigServiceImpl.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java diff --git a/pom.xml b/pom.xml index 43da0c8d03..a343309726 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ - + yudao-module-iot ${project.artifactId} diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 1f3c9144bf..2a14c88b87 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -63,6 +63,10 @@ opengauss-jdbc true + + com.taosdata.jdbc + taos-jdbcdriver + com.alibaba diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index ed9b0ef138..e85d4b368f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -39,12 +39,13 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); - // ========== 插件信息 1-050-006-000 ========== - ErrorCode PLUGIN_INFO_NOT_EXISTS = new ErrorCode(1_050_006_000, "插件信息不存在"); + // ========== 插件配置 1-050-006-000 ========== + ErrorCode PLUGIN_CONFIG_NOT_EXISTS = new ErrorCode(1_050_006_000, "插件配置不存在"); ErrorCode PLUGIN_INSTALL_FAILED = new ErrorCode(1_050_006_001, "插件安装失败"); ErrorCode PLUGIN_INSTALL_FAILED_FILE_NAME_NOT_MATCH = new ErrorCode(1_050_006_002, "插件安装失败,文件名与原插件id不匹配"); - ErrorCode PLUGIN_INFO_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件"); + ErrorCode PLUGIN_CONFIG_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件"); ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效"); + ErrorCode PLUGIN_CONFIG_KEY_DUPLICATE = new ErrorCode(1_050_006_005, "插件标识已存在"); // ========== 插件实例 1-050-007-000 ========== diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index cc3141939d..805205a167 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -52,11 +52,6 @@ yudao-spring-boot-starter-redis - - com.taosdata.jdbc - taos-jdbcdriver - - cn.iocoder.boot diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java new file mode 100644 index 0000000000..e21b102410 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugin; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigImportReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigStatusReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 插件配置") +@RestController +@RequestMapping("/iot/plugin-config") +@Validated +public class PluginConfigController { + + @Resource + private IotPluginConfigService pluginConfigService; + + @PostMapping("/create") + @Operation(summary = "创建插件配置") + @PreAuthorize("@ss.hasPermission('iot:plugin-config:create')") + public CommonResult createPluginConfig(@Valid @RequestBody PluginConfigSaveReqVO createReqVO) { + return success(pluginConfigService.createPluginConfig(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新插件配置") + @PreAuthorize("@ss.hasPermission('iot:plugin-config:update')") + public CommonResult updatePluginConfig(@Valid @RequestBody PluginConfigSaveReqVO updateReqVO) { + pluginConfigService.updatePluginConfig(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除插件配置") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:plugin-config:delete')") + public CommonResult deletePluginConfig(@RequestParam("id") Long id) { + pluginConfigService.deletePluginConfig(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得插件配置") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:plugin-config:query')") + public CommonResult getPluginConfig(@RequestParam("id") Long id) { + IotPluginConfigDO pluginConfig = pluginConfigService.getPluginConfig(id); + return success(BeanUtils.toBean(pluginConfig, PluginConfigRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得插件配置分页") + @PreAuthorize("@ss.hasPermission('iot:plugin-config:query')") + public CommonResult> getPluginConfigPage(@Valid PluginConfigPageReqVO pageReqVO) { + PageResult pageResult = pluginConfigService.getPluginConfigPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, PluginConfigRespVO.class)); + } + + @PostMapping("/upload-file") + @Operation(summary = "上传插件文件") + @PreAuthorize("@ss.hasPermission('iot:plugin-config:update')") + public CommonResult uploadFile(@Valid PluginConfigImportReqVO reqVO) { + pluginConfigService.uploadFile(reqVO.getId(), reqVO.getFile()); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "修改插件状态") + @PreAuthorize("@ss.hasPermission('iot:plugin-config:update')") + public CommonResult updatePluginConfigStatus(@Valid @RequestBody PluginConfigStatusReqVO reqVO) { + pluginConfigService.updatePluginStatus(reqVO.getId(), reqVO.getStatus()); + return success(true); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java deleted file mode 100644 index 9b78001595..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugin; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoImportReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoRespVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - IoT 插件信息") -@RestController -@RequestMapping("/iot/plugin-info") -@Validated -public class PluginInfoController { - - @Resource - private IotPluginInfoService pluginInfoService; - - @PostMapping("/create") - @Operation(summary = "创建插件信息") - @PreAuthorize("@ss.hasPermission('iot:plugin-info:create')") - public CommonResult createPluginInfo(@Valid @RequestBody PluginInfoSaveReqVO createReqVO) { - return success(pluginInfoService.createPluginInfo(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新插件信息") - @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") - public CommonResult updatePluginInfo(@Valid @RequestBody PluginInfoSaveReqVO updateReqVO) { - pluginInfoService.updatePluginInfo(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除插件信息") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('iot:plugin-info:delete')") - public CommonResult deletePluginInfo(@RequestParam("id") Long id) { - pluginInfoService.deletePluginInfo(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得插件信息") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')") - public CommonResult getPluginInfo(@RequestParam("id") Long id) { - IotPluginInfoDO pluginInfo = pluginInfoService.getPluginInfo(id); - return success(BeanUtils.toBean(pluginInfo, PluginInfoRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得插件信息分页") - @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')") - public CommonResult> getPluginInfoPage(@Valid PluginInfoPageReqVO pageReqVO) { - PageResult pageResult = pluginInfoService.getPluginInfoPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class)); - } - - @PostMapping("/upload-file") - @Operation(summary = "上传插件文件") - @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") - public CommonResult uploadFile(@Valid PluginInfoImportReqVO reqVO) { - pluginInfoService.uploadFile(reqVO.getId(), reqVO.getFile()); - return success(true); - } - - // TODO @haohao:要不独立一个 VO,不用 PluginInfoSaveReqVO - @PutMapping("/update-status") - @Operation(summary = "修改插件状态") - @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')") - public CommonResult updateUserStatus(@Valid @RequestBody PluginInfoSaveReqVO reqVO) { - pluginInfoService.updatePluginStatus(reqVO.getId(), reqVO.getStatus()); - return success(true); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoImportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigImportReqVO.java similarity index 83% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoImportReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigImportReqVO.java index fc2f8da8a9..b9b277a542 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoImportReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigImportReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; @@ -7,7 +7,7 @@ import org.springframework.web.multipart.MultipartFile; @Schema(description = "管理后台 - IoT 插件上传 Request VO") @Data -public class PluginInfoImportReqVO { +public class PluginConfigImportReqVO { @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") private Long id; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigPageReqVO.java similarity index 71% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoPageReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigPageReqVO.java index cac975be18..1666d5d6bc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigPageReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.validation.InEnum; @@ -6,9 +6,9 @@ import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - IoT 插件信息分页 Request VO") +@Schema(description = "管理后台 - IoT 插件配置分页 Request VO") @Data -public class PluginInfoPageReqVO extends PageParam { +public class PluginConfigPageReqVO extends PageParam { @Schema(description = "插件名称", example = "http") private String name; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigRespVO.java similarity index 90% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigRespVO.java index 685d408de2..2b8c4dcde8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigRespVO.java @@ -1,13 +1,13 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -@Schema(description = "管理后台 - IoT 插件信息 Response VO") +@Schema(description = "管理后台 - IoT 插件配置 Response VO") @Data -public class PluginInfoRespVO { +public class PluginConfigRespVO { @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") private Long id; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigSaveReqVO.java similarity index 91% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigSaveReqVO.java index c2a16ac496..e48869d645 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigSaveReqVO.java @@ -1,13 +1,13 @@ -package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info; +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - IoT 插件信息新增/修改 Request VO") +@Schema(description = "管理后台 - IoT 插件配置新增/修改 Request VO") @Data -public class PluginInfoSaveReqVO { +public class PluginConfigSaveReqVO { // TODO @haohao:新增的字段有点多,每个都需要哇? diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java new file mode 100644 index 0000000000..eae4aa0a2e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 插件配置状态 Request VO") +@Data +public class PluginConfigStatusReqVO { + + @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") + private Long id; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED) + @InEnum(IotPluginStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java similarity index 82% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java index 3a9c588d4a..d0695f8963 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugin; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; @@ -9,21 +10,20 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -// TODO @haohao:建议 IotPluginInfoDO 改成 IotPluginConfigDO,插件配置。项目里,暂时没有用 info 作为配置的哈。 /** - * IoT 插件信息 DO + * IoT 插件配置 DO * * @author 芋道源码 */ -@TableName("iot_plugin_info") -@KeySequence("iot_plugin_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName("iot_plugin_config") +@KeySequence("iot_plugin_config_seq") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class IotPluginInfoDO extends BaseDO { +public class IotPluginConfigDO extends BaseDO { /** * 主键 ID @@ -73,7 +73,7 @@ public class IotPluginInfoDO extends BaseDO { /** * 状态 *

- * 枚举 {@link IotPluginStatusEnum} + * 枚举 {@link CommonStatusEnum} */ private Integer status; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java index 1def801855..71d741b052 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java @@ -31,7 +31,7 @@ public class IotPluginInstanceDO extends BaseDO { /** * 插件编号 *

- * 关联 {@link IotPluginInfoDO#getId()} + * 关联 {@link IotPluginConfigDO#getId()} */ private Long pluginId; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginConfigMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginConfigMapper.java new file mode 100644 index 0000000000..0e2163a3fa --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginConfigMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.plugin; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface IotPluginConfigMapper extends BaseMapperX { + + default PageResult selectPage(PluginConfigPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotPluginConfigDO::getName, reqVO.getName()) + .eqIfPresent(IotPluginConfigDO::getStatus, reqVO.getStatus()) + .orderByDesc(IotPluginConfigDO::getId)); + } + + default List selectListByStatusAndDeployType(Integer status, Integer deployType) { + return selectList(new LambdaQueryWrapperX() + .eq(IotPluginConfigDO::getStatus, status) + .eq(IotPluginConfigDO::getDeployType, deployType) + .orderByAsc(IotPluginConfigDO::getId)); + } + + default IotPluginConfigDO selectByPluginKey(String pluginKey) { + return selectOne(IotPluginConfigDO::getPluginKey, pluginKey); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInfoMapper.java deleted file mode 100644 index 88058185f0..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInfoMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.iot.dal.mysql.plugin; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface IotPluginInfoMapper extends BaseMapperX { - - default PageResult selectPage(PluginInfoPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(IotPluginInfoDO::getName, reqVO.getName()) - .eqIfPresent(IotPluginInfoDO::getStatus, reqVO.getStatus()) - .orderByDesc(IotPluginInfoDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(new LambdaQueryWrapperX() - .eq(IotPluginInfoDO::getStatus, status) - .orderByAsc(IotPluginInfoDO::getId)); - } - - default IotPluginInfoDO selectByPluginKey(String pluginKey) { - return selectOne(IotPluginInfoDO::getPluginKey, pluginKey); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java index b695cbe39f..0a2812ac87 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.framework.plugin.config; import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStartRunner; import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStateListener; -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService; +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginConfigService; import lombok.extern.slf4j.Slf4j; import org.pf4j.spring.SpringPluginManager; import org.springframework.beans.factory.annotation.Value; @@ -22,8 +22,8 @@ public class IotPluginConfiguration { @Bean public IotPluginStartRunner pluginStartRunner(SpringPluginManager pluginManager, - IotPluginInfoService pluginInfoService) { - return new IotPluginStartRunner(pluginManager, pluginInfoService); + IotPluginConfigService pluginConfigService) { + return new IotPluginStartRunner(pluginManager, pluginConfigService); } // TODO @芋艿:需要 review 下 @@ -31,7 +31,6 @@ public class IotPluginConfiguration { public SpringPluginManager pluginManager(@Value("${pf4j.pluginsDir:pluginsDir}") String pluginsDir) { log.info("[init][实例化 SpringPluginManager]"); SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) { -// SpringPluginManager springPluginManager = new SpringPluginManager() { @Override public void startPlugins() { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java index f3654bc305..8036eb2d47 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java @@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.iot.framework.plugin.core; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService; +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginConfigService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.pf4j.spring.SpringPluginManager; @@ -25,28 +25,29 @@ public class IotPluginStartRunner implements ApplicationRunner { private final SpringPluginManager springPluginManager; - private final IotPluginInfoService pluginInfoService; + private final IotPluginConfigService pluginConfigService; @Override public void run(ApplicationArguments args) { - List pluginInfoList = TenantUtils.executeIgnore( - // TODO @haohao:需要查询部署类型哈 - () -> pluginInfoService.getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus())); - if (CollUtil.isEmpty(pluginInfoList)) { + List pluginConfigList = TenantUtils.executeIgnore( + // 查询运行中且部署类型为 JAR 的插件 + () -> pluginConfigService.getPluginConfigListByStatusAndDeployType( + IotPluginStatusEnum.RUNNING.getStatus(), IotPluginDeployTypeEnum.JAR.getDeployType())); + if (CollUtil.isEmpty(pluginConfigList)) { log.info("[run][没有需要启动的插件]"); return; } // 遍历插件列表,逐个启动 - pluginInfoList.forEach(pluginInfo -> { + pluginConfigList.forEach(pluginConfig -> { try { - log.info("[run][插件({}) 启动开始]", pluginInfo.getPluginKey()); - springPluginManager.startPlugin(pluginInfo.getPluginKey()); - log.info("[run][插件({}) 启动完成]", pluginInfo.getPluginKey()); + log.info("[run][插件({}) 启动开始]", pluginConfig.getPluginKey()); + springPluginManager.startPlugin(pluginConfig.getPluginKey()); + log.info("[run][插件({}) 启动完成]", pluginConfig.getPluginKey()); } catch (Exception e) { - log.error("[run][插件({}) 启动异常]", pluginInfo.getPluginKey(), e); + log.error("[run][插件({}) 启动异常]", pluginConfig.getPluginKey(), e); } }); } -} +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigService.java new file mode 100644 index 0000000000..8b6610f150 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigService.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.iot.service.plugin; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * IoT 插件配置 Service 接口 + * + * @author haohao + */ +public interface IotPluginConfigService { + + /** + * 创建插件配置 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createPluginConfig(@Valid PluginConfigSaveReqVO createReqVO); + + /** + * 更新插件配置 + * + * @param updateReqVO 更新信息 + */ + void updatePluginConfig(@Valid PluginConfigSaveReqVO updateReqVO); + + /** + * 删除插件配置 + * + * @param id 编号 + */ + void deletePluginConfig(Long id); + + /** + * 获得插件配置 + * + * @param id 编号 + * @return 插件配置 + */ + IotPluginConfigDO getPluginConfig(Long id); + + /** + * 获得插件配置分页 + * + * @param pageReqVO 分页查询 + * @return 插件配置分页 + */ + PageResult getPluginConfigPage(PluginConfigPageReqVO pageReqVO); + + /** + * 上传插件的 JAR 包 + * + * @param id 插件id + * @param file 文件 + */ + void uploadFile(Long id, MultipartFile file); + + /** + * 更新插件的状态 + * + * @param id 插件id + * @param status 状态 {@link IotPluginStatusEnum} + */ + void updatePluginStatus(Long id, Integer status); + + /** + * 获得插件配置列表 + * + * @return 插件配置列表 + */ + List getPluginConfigList(); + + /** + * 根据状态和部署类型获得插件配置列表 + * + * @param status 状态 {@link IotPluginStatusEnum} + * @param deployType 部署类型 {@link IotPluginDeployTypeEnum} + * @return 插件配置列表 + */ + List getPluginConfigListByStatusAndDeployType(Integer status, Integer deployType); + + /** + * 根据插件包标识符获取插件配置 + * + * @param pluginKey 插件包标识符 + * @return 插件配置 + */ + IotPluginConfigDO getPluginConfigByPluginKey(@NotEmpty(message = "插件包标识符不能为空") String pluginKey); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigServiceImpl.java new file mode 100644 index 0000000000..18376bc578 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginConfigServiceImpl.java @@ -0,0 +1,188 @@ +package cn.iocoder.yudao.module.iot.service.plugin; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; +import cn.iocoder.yudao.module.iot.dal.mysql.plugin.IotPluginConfigMapper; +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPluginManager; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; +/** + * IoT 插件配置 Service 实现类 + * + * @author haohao + */ +@Service +@Validated +@Slf4j +public class IotPluginConfigServiceImpl implements IotPluginConfigService { + + @Resource + private IotPluginConfigMapper pluginConfigMapper; + + @Resource + private IotPluginInstanceService pluginInstanceService; + + @Resource + private SpringPluginManager springPluginManager; + + @Override + public Long createPluginConfig(PluginConfigSaveReqVO createReqVO) { + // 1. 校验插件标识唯一性:确保没有其他配置使用相同的 pluginKey(新建时 id 为 null) + validatePluginKeyUnique(null, createReqVO.getPluginKey()); + IotPluginConfigDO pluginConfig = BeanUtils.toBean(createReqVO, IotPluginConfigDO.class); + // 2. 插入插件配置到数据库 + pluginConfigMapper.insert(pluginConfig); + return pluginConfig.getId(); + } + + @Override + public void updatePluginConfig(PluginConfigSaveReqVO updateReqVO) { + // 1. 校验插件配置是否存在:根据传入 ID 判断记录是否存在 + validatePluginConfigExists(updateReqVO.getId()); + // 2. 校验插件标识唯一性:确保更新后的 pluginKey 没有被其他记录占用 + validatePluginKeyUnique(updateReqVO.getId(), updateReqVO.getPluginKey()); + // 3. 将更新请求对象转换为插件配置数据对象 + IotPluginConfigDO updateObj = BeanUtils.toBean(updateReqVO, IotPluginConfigDO.class); + pluginConfigMapper.updateById(updateObj); + } + + /** + * 校验插件标识唯一性 + * + * @param id 当前插件配置的 ID(如果为 null 则说明为新建操作) + * @param pluginKey 待校验的插件标识 + */ + private void validatePluginKeyUnique(Long id, String pluginKey) { + // 1. 根据 pluginKey 从数据库中查询已有的插件配置 + IotPluginConfigDO pluginConfig = pluginConfigMapper.selectByPluginKey(pluginKey); + // 2. 如果查询到记录且记录的 ID 与当前 ID 不相同,则认为存在重复,抛出异常 + if (pluginConfig != null && !pluginConfig.getId().equals(id)) { + throw exception(PLUGIN_CONFIG_KEY_DUPLICATE); + } + } + + @Override + public void deletePluginConfig(Long id) { + // 1. 校验存在 + IotPluginConfigDO pluginConfigDO = validatePluginConfigExists(id); + // 2. 未开启状态,才允许删除 + if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginConfigDO.getStatus())) { + throw exception(PLUGIN_CONFIG_DELETE_FAILED_RUNNING); + } + + // 3. 卸载插件 + pluginInstanceService.stopAndUnloadPlugin(pluginConfigDO.getPluginKey()); + // 4. 删除插件文件 + pluginInstanceService.deletePluginFile(pluginConfigDO); + + // 5. 删除插件配置 + pluginConfigMapper.deleteById(id); + } + + /** + * 校验插件配置是否存在 + * + * @param id 插件配置编号 + * @return 插件配置 + */ + private IotPluginConfigDO validatePluginConfigExists(Long id) { + IotPluginConfigDO pluginConfig = pluginConfigMapper.selectById(id); + if (pluginConfig == null) { + throw exception(PLUGIN_CONFIG_NOT_EXISTS); + } + return pluginConfig; + } + + @Override + public IotPluginConfigDO getPluginConfig(Long id) { + return pluginConfigMapper.selectById(id); + } + + @Override + public PageResult getPluginConfigPage(PluginConfigPageReqVO pageReqVO) { + return pluginConfigMapper.selectPage(pageReqVO); + } + + @Override + public void uploadFile(Long id, MultipartFile file) { + // 1. 校验插件配置是否存在 + IotPluginConfigDO pluginConfigDO = validatePluginConfigExists(id); + + // 2.1 停止并卸载旧的插件 + pluginInstanceService.stopAndUnloadPlugin(pluginConfigDO.getPluginKey()); + // 2.2 上传新的插件文件,更新插件启用状态文件 + String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); + + // 3. 校验 file 相关参数,是否完整 + validatePluginConfigFile(pluginKeyNew); + + // 4. 更新插件配置 + IotPluginConfigDO updatedPluginConfig = new IotPluginConfigDO() + .setId(pluginConfigDO.getId()) + .setPluginKey(pluginKeyNew) + .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈? + .setFileName(file.getOriginalFilename()) + .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来? + .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) + .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) + .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); + pluginConfigMapper.updateById(updatedPluginConfig); + } + + /** + * 校验 file 相关参数 + * + * @param pluginKeyNew 插件标识符 + */ + private void validatePluginConfigFile(String pluginKeyNew) { + // TODO @haohao:校验 file 相关参数,是否完整,类似:version 之类是不是可以解析到 + PluginWrapper plugin = springPluginManager.getPlugin(pluginKeyNew); + if (plugin == null) { + throw exception(PLUGIN_INSTALL_FAILED); + } + if (plugin.getDescriptor().getVersion() == null) { + throw exception(PLUGIN_INSTALL_FAILED); + } + } + + @Override + public void updatePluginStatus(Long id, Integer status) { + // 1. 校验插件配置是否存在 + IotPluginConfigDO pluginConfigDo = validatePluginConfigExists(id); + + // 2. 更新插件状态 + pluginInstanceService.updatePluginStatus(pluginConfigDo, status); + + // 3. 更新数据库中的插件状态 + pluginConfigMapper.updateById(new IotPluginConfigDO().setId(id).setStatus(status)); + } + + @Override + public List getPluginConfigList() { + return pluginConfigMapper.selectList(); + } + + @Override + public List getPluginConfigListByStatusAndDeployType(Integer status, Integer deployType) { + return pluginConfigMapper.selectListByStatusAndDeployType(status, deployType); + } + + @Override + public IotPluginConfigDO getPluginConfigByPluginKey(String pluginKey) { + return pluginConfigMapper.selectByPluginKey(pluginKey); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java deleted file mode 100644 index 1a8654ebc6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugin; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; - -/** - * IoT 插件信息 Service 接口 - * - * @author haohao - */ -public interface IotPluginInfoService { - - /** - * 创建插件信息 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createPluginInfo(@Valid PluginInfoSaveReqVO createReqVO); - - /** - * 更新插件信息 - * - * @param updateReqVO 更新信息 - */ - void updatePluginInfo(@Valid PluginInfoSaveReqVO updateReqVO); - - /** - * 删除插件信息 - * - * @param id 编号 - */ - void deletePluginInfo(Long id); - - /** - * 获得插件信息 - * - * @param id 编号 - * @return 插件信息 - */ - IotPluginInfoDO getPluginInfo(Long id); - - /** - * 获得插件信息分页 - * - * @param pageReqVO 分页查询 - * @return 插件信息分页 - */ - PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO); - - /** - * 上传插件的 JAR 包 - * - * @param id 插件id - * @param file 文件 - */ - void uploadFile(Long id, MultipartFile file); - - /** - * 更新插件的状态 - * - * @param id 插件id - * @param status 状态 {@link IotPluginStatusEnum} - */ - void updatePluginStatus(Long id, Integer status); - - /** - * 获得插件信息列表 - * - * @return 插件信息列表 - */ - List getPluginInfoList(); - - /** - * 根据状态获得插件信息列表 - * - * @param status 状态 {@link IotPluginStatusEnum} - * @return 插件信息列表 - */ - List getPluginInfoListByStatus(Integer status); - - IotPluginInfoDO getPluginInfoByPluginKey(@NotEmpty(message = "插件包标识符不能为空") String pluginKey); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java deleted file mode 100644 index 400f1e39ea..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java +++ /dev/null @@ -1,158 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.plugin; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.info.PluginInfoSaveReqVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; -import cn.iocoder.yudao.module.iot.dal.mysql.plugin.IotPluginInfoMapper; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.pf4j.spring.SpringPluginManager; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.multipart.MultipartFile; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_DELETE_FAILED_RUNNING; -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_NOT_EXISTS; - -/** - * IoT 插件信息 Service 实现类 - * - * @author haohao - */ -@Service -@Validated -@Slf4j -public class IotPluginInfoServiceImpl implements IotPluginInfoService { - - @Resource - private IotPluginInfoMapper pluginInfoMapper; - - @Resource - private IotPluginInstanceService pluginInstanceService; - - @Resource - private SpringPluginManager springPluginManager; - - @Override - public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) { - // TODO @haohao:pluginKey 唯一值 - IotPluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, IotPluginInfoDO.class); - pluginInfoMapper.insert(pluginInfo); - return pluginInfo.getId(); - } - - @Override - public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) { - // 校验存在 - validatePluginInfoExists(updateReqVO.getId()); - // 更新 - IotPluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, IotPluginInfoDO.class); - pluginInfoMapper.updateById(updateObj); - } - - @Override - public void deletePluginInfo(Long id) { - // 1.1 校验存在 - IotPluginInfoDO pluginInfoDO = validatePluginInfoExists(id); - // 1.2 未开启状态,才允许删除 - if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) { - throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING); - } - - // 2.1 卸载插件 - pluginInstanceService.stopAndUnloadPlugin(pluginInfoDO.getPluginKey()); - // 2.2 删除插件文件 - pluginInstanceService.deletePluginFile(pluginInfoDO); - - // 3. 删除插件信息 - pluginInfoMapper.deleteById(id); - } - - private IotPluginInfoDO validatePluginInfoExists(Long id) { - IotPluginInfoDO pluginInfo = pluginInfoMapper.selectById(id); - if (pluginInfo == null) { - throw exception(PLUGIN_INFO_NOT_EXISTS); - } - return pluginInfo; - } - - @Override - public IotPluginInfoDO getPluginInfo(Long id) { - return pluginInfoMapper.selectById(id); - } - - @Override - public PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO) { - return pluginInfoMapper.selectPage(pageReqVO); - } - - @Override - public void uploadFile(Long id, MultipartFile file) { - // 1. 校验插件信息是否存在 - IotPluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - // TODO @haohao:最好校验下 file 相关参数,是否完整,类似:version 之类是不是可以解析到 - - // 2.1 停止并卸载旧的插件 - pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey()); - // 2.2 上传新的插件文件,更新插件启用状态文件 - String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); - - // 3. 更新插件信息 - updatePluginInfo(pluginInfoDo, pluginKeyNew, file); - } - - // TODO @haohao:这个方法,要不合并到 uploadFile 里; - /** - * 更新插件信息 - * - * @param pluginInfoDo 插件信息 - * @param pluginKeyNew 插件标识符 - * @param file 文件 - */ - private void updatePluginInfo(IotPluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) { - IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO() - .setId(pluginInfoDo.getId()) - .setPluginKey(pluginKeyNew) - .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈? - .setFileName(file.getOriginalFilename()) - .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来? - .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()) - .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion()) - .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription()); - pluginInfoMapper.updateById(updatedPluginInfo); - } - - @Override - public void updatePluginStatus(Long id, Integer status) { - // 1. 校验插件信息是否存在 - IotPluginInfoDO pluginInfoDo = validatePluginInfoExists(id); - - // 2. 更新插件状态 - pluginInstanceService.updatePluginStatus(pluginInfoDo, status); - - // 3. 更新数据库中的插件状态 - pluginInfoMapper.updateById(new IotPluginInfoDO().setId(id).setStatus(status)); - } - - @Override - public List getPluginInfoList() { - return pluginInfoMapper.selectList(); - } - - @Override - public List getPluginInfoListByStatus(Integer status) { - return pluginInfoMapper.selectListByStatus(status); - } - - @Override - public IotPluginInfoDO getPluginInfoByPluginKey(String pluginKey) { - return pluginInfoMapper.selectByPluginKey(pluginKey); - } - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java index 1e48070806..56e1bf0f08 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceService.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.service.plugin; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; import org.springframework.web.multipart.MultipartFile; @@ -38,9 +38,9 @@ public interface IotPluginInstanceService { /** * 删除插件文件 * - * @param pluginInfoDo 插件信息 + * @param pluginConfigDO 插件配置 */ - void deletePluginFile(IotPluginInfoDO pluginInfoDo); + void deletePluginFile(IotPluginConfigDO pluginConfigDO); /** * 上传并加载新的插件文件 @@ -53,10 +53,10 @@ public interface IotPluginInstanceService { /** * 更新插件状态 * - * @param pluginInfoDo 插件信息 + * @param pluginConfigDO 插件配置 * @param status 新状态 */ - void updatePluginStatus(IotPluginInfoDO pluginInfoDo, Integer status); + void updatePluginStatus(IotPluginConfigDO pluginConfigDO, Integer status); // ========== 设备与插件的映射操作 ========== diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java index 6742eccd79..a3edbc1757 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java @@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; import cn.iocoder.yudao.module.iot.dal.mysql.plugin.IotPluginInstanceMapper; import cn.iocoder.yudao.module.iot.dal.redis.plugin.DevicePluginProcessIdRedisDAO; @@ -45,7 +45,7 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { @Resource @Lazy // 延迟加载,避免循环依赖 - private IotPluginInfoService pluginInfoService; + private IotPluginConfigService pluginConfigService; @Resource private IotPluginInstanceMapper pluginInstanceMapper; @@ -79,7 +79,7 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { } // 情况二:不存在,则创建 - IotPluginInfoDO info = pluginInfoService.getPluginInfoByPluginKey(heartbeatReqDTO.getPluginKey()); + IotPluginConfigDO info = pluginConfigService.getPluginConfigByPluginKey(heartbeatReqDTO.getPluginKey()); if (info == null) { log.error("[heartbeatPluginInstance][心跳({}) 对应的插件不存在]", heartbeatReqDTO); return; @@ -129,18 +129,18 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { } @Override - public void deletePluginFile(IotPluginInfoDO pluginInfoDO) { - File file = new File(pluginsDir, pluginInfoDO.getFileName()); + public void deletePluginFile(IotPluginConfigDO pluginConfigDO) { + File file = new File(pluginsDir, pluginConfigDO.getFileName()); if (!file.exists()) { return; } try { TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕 if (!file.delete()) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName()); + log.error("[deletePluginFile][删除插件文件({}) 失败]", pluginConfigDO.getFileName()); } } catch (InterruptedException e) { - log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e); + log.error("[deletePluginFile][删除插件文件({}) 失败]", pluginConfigDO.getFileName(), e); } } @@ -171,13 +171,13 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { } @Override - public void updatePluginStatus(IotPluginInfoDO pluginInfoDo, Integer status) { - String pluginKey = pluginInfoDo.getPluginKey(); + public void updatePluginStatus(IotPluginConfigDO pluginConfigDO, Integer status) { + String pluginKey = pluginConfigDO.getPluginKey(); PluginWrapper plugin = pluginManager.getPlugin(pluginKey); if (plugin == null) { // 插件不存在且状态为停止,抛出异常 - if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) { + if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginConfigDO.getStatus())) { throw exception(ErrorCodeConstants.PLUGIN_STATUS_INVALID); } return; diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index efd53c84a5..492e31db56 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -109,11 +109,11 @@ - - - - - + + cn.iocoder.boot + yudao-module-iot-biz + ${revision} + From 00edd5a7246b5c5dd91f6d7f88723c6a0a22495a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 5 Feb 2025 23:15:43 +0800 Subject: [PATCH 156/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20ota=20?= =?UTF-8?q?=E7=9A=84=E8=A1=A8=E7=BB=93=E6=9E=84=E8=AE=BE=E8=AE=A1=EF=BC=88?= =?UTF-8?q?90%=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/ota/IotOtaFirmwareDO.java | 81 ++++++++++++++++++ .../dataobject/ota/IotOtaUpgradeRecordDO.java | 83 +++++++++++++++++++ .../dataobject/ota/IotOtaUpgradeTaskDO.java | 63 ++++++++++++++ .../dal/dataobject/rule/IotAlertRecordDO.java | 1 + .../IotDeviceDownstreamServiceImpl.java | 1 + 5 files changed, 229 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java new file mode 100644 index 0000000000..12e5147edd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.ota; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * IoT OTA 固件 DO + * + * @see https://help.aliyun.com/zh/iot/user-guide/ota-upgrade-overview + * + * @author 芋道源码 + */ +@TableName(value = "iot_ota_firmware", autoResultMap = true) +@KeySequence("iot_ota_firmware_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotOtaFirmwareDO extends BaseDO { + + /** + * 固件编号 + */ + @TableField + private Long id; + /** + * 固件名称 + */ + private String name; + /** + * 固件版本 + */ + private String description; + /** + * 版本号 + */ + private String version; + + /** + * 产品编号 + * + * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} + */ + private String productId; + /** + * 产品标识 + * + * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()} + */ + private String productKey; + + /** + * 签名方式 + * + * 例如说:MD5、SHA256 + */ + private String signMethod; + /** + * 固件文件签名 + */ + private String fileSign; + /** + * 固件文件大小 + */ + private Long fileSize; + /** + * 固件文件 URL + */ + private String fileUrl; + + /** + * 自定义信息,建议使用 JSON 格式 + */ + private String information; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java new file mode 100644 index 0000000000..b4868f3875 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.ota; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * IoT OTA 升级记录 DO + * + * @author 芋道源码 + */ +@TableName(value = "iot_ota_upgrade_record", autoResultMap = true) +@KeySequence("iot_ota_upgrade_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotOtaUpgradeRecordDO extends BaseDO { + + @TableId + private Long id; + + /** + * 固件编号 + * + * 关联 {@link IotOtaFirmwareDO#getId()} + */ + private Long firmwareId; + /** + * 任务编号 + * + * 关联 {@link IotOtaUpgradeTaskDO#getId()} + */ + private Long taskId; + + /** + * 升级状态 + * + * TODO + */ + private Integer status; + /** + * 升级进度,百分比 + */ + private Integer progress; + + /** + * 产品标识 + * + * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} + */ + private String productKey; + /** + * 设备名称 + * + * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} + */ + private String deviceName; + /** + * 设备编号 + * + * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} + */ + private String deviceId; + + /** + * 升级开始时间 + */ + private LocalDateTime startTime; + /** + * 升级结束时间 + */ + private LocalDateTime endTime; + + + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java new file mode 100644 index 0000000000..529accfdd5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.dal.dataobject.ota; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.List; + +/** + * IoT OTA 升级任务 DO + * + * @author 芋道源码 + */ +@TableName(value = "iot_ota_upgrade_task", autoResultMap = true) +@KeySequence("iot_ota_upgrade_task_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotOtaUpgradeTaskDO extends BaseDO { + + /** + * 任务编号 + */ + @TableField + private Long id; + /** + * 任务名称 + */ + private String name; + /** + * 任务描述 + */ + private String description; + + /** + * 固件编号 + * + * 关联 {@link IotOtaFirmwareDO#getId()} + */ + private Long firmwareId; + + /** + * 任务类型 + * + * TODO @芋艿:1-全部、2-指定设备 + */ + private Integer type; + /** + * 选中的设备名字数组 + * + * 关联 {@link IotDeviceDO#getDeviceName()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List deviceNames; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java index 2fc01ba7d3..fbcf1fe79d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java @@ -63,6 +63,7 @@ public class IotAlertRecordDO extends BaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private IotDeviceMessage deviceMessage; + // TODO @芋艿:换成枚举,枚举对应 ApiErrorLogProcessStatusEnum /** * 处理状态 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 006636c230..bac2ef6c90 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -57,6 +57,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic public IotDeviceMessage downstreamDevice(IotDeviceDownstreamReqVO downstreamReqVO) { // 校验设备是否存在 IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); + // TODO @芋艿:离线设备,不允许推送 // TODO 芋艿:父设备的处理 IotDeviceDO parentDevice = null; From 8ced4a0a2cee3dbee171ecdcfb7766ede875f2d5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Feb 2025 22:00:34 +0800 Subject: [PATCH 157/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20ota=20?= =?UTF-8?q?=E7=9A=84=E8=A1=A8=E7=BB=93=E6=9E=84=E8=AE=BE=E8=AE=A1=EF=BC=88?= =?UTF-8?q?100%=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ota/IotOtaUpgradeRecordStatusEnum.java | 38 +++++++++++++++++++ .../enums/ota/IotOtaUpgradeTaskScopeEnum.java | 33 ++++++++++++++++ .../ota/IotOtaUpgradeTaskStatusEnum.java | 35 +++++++++++++++++ .../dataobject/ota/IotOtaUpgradeRecordDO.java | 30 ++++++++------- .../dataobject/ota/IotOtaUpgradeTaskDO.java | 13 +++++-- 5 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeRecordStatusEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskScopeEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskStatusEnum.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeRecordStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeRecordStatusEnum.java new file mode 100644 index 0000000000..e809a7e5b2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeRecordStatusEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.iot.enums.ota; + + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * IoT OTA 升级记录的范围枚举 + * + * @author haohao + */ +@RequiredArgsConstructor +@Getter +public enum IotOtaUpgradeRecordStatusEnum implements ArrayValuable { + + PENDING(0), // 待推送 + PUSHED(10), // 已推送 + UPGRADING(20), // 升级中 + SUCCESS(30), // 升级成功 + FAILURE(40), // 升级失败 + CANCELED(50),; // 已取消 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotOtaUpgradeRecordStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 范围 + */ + private final Integer status; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskScopeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskScopeEnum.java new file mode 100644 index 0000000000..6dccbb041c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskScopeEnum.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.iot.enums.ota; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * IoT OTA 升级任务的范围枚举 + * + * @author haohao + */ +@RequiredArgsConstructor +@Getter +public enum IotOtaUpgradeTaskScopeEnum implements ArrayValuable { + + ALL(1), // 全部设备:只包括当前产品下的设备,不包括未来创建的设备 + SELECT(2); // 指定设备 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotOtaUpgradeTaskScopeEnum::getScope).toArray(Integer[]::new); + + /** + * 范围 + */ + private final Integer scope; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskStatusEnum.java new file mode 100644 index 0000000000..78af16cb20 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ota/IotOtaUpgradeTaskStatusEnum.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.enums.ota; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * IoT OTA 升级任务的范围枚举 + * + * @author haohao + */ +@RequiredArgsConstructor +@Getter +public enum IotOtaUpgradeTaskStatusEnum implements ArrayValuable { + + IN_PROGRESS(10), // 进行中:升级中 + COMPLETED(20), // 已完成:已结束,全部升级完成 + INCOMPLETE(21), // 未完成:已结束,部分升级完成 + CANCELED(30),; // 已取消:一般是主动取消任务 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotOtaUpgradeTaskStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 范围 + */ + private final Integer status; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java index b4868f3875..14924a7866 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java @@ -39,17 +39,6 @@ public class IotOtaUpgradeRecordDO extends BaseDO { */ private Long taskId; - /** - * 升级状态 - * - * TODO - */ - private Integer status; - /** - * 升级进度,百分比 - */ - private Integer progress; - /** * 产品标识 * @@ -69,6 +58,23 @@ public class IotOtaUpgradeRecordDO extends BaseDO { */ private String deviceId; + /** + * 升级状态 + * + * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} + */ + private Integer status; + /** + * 升级进度,百分比 + */ + private Integer progress; + /** + * 升级进度描述 + * + * 注意,只记录设备最后一次的升级进度描述 + * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志 + */ + private String description; /** * 升级开始时间 */ @@ -78,6 +84,4 @@ public class IotOtaUpgradeRecordDO extends BaseDO { */ private LocalDateTime endTime; - - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java index 529accfdd5..7d9e4425db 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java @@ -47,11 +47,18 @@ public class IotOtaUpgradeTaskDO extends BaseDO { private Long firmwareId; /** - * 任务类型 + * 任务状态 * - * TODO @芋艿:1-全部、2-指定设备 + * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum} */ - private Integer type; + private Integer status; + + /** + * 升级范围 + * + * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum} + */ + private Integer scope; /** * 选中的设备名字数组 * From 8fac009d4bca81f785218cc323fd0ad8292fa828 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 7 Feb 2025 09:44:41 +0800 Subject: [PATCH 158/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E5=9F=BA=E4=BA=8E=20review=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=20ota=20=E7=9A=84=E8=A1=A8=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/dal/dataobject/device/IotDeviceDO.java | 8 +++++--- .../iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java | 7 +++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index 351e001857..d991454765 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler; import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -116,9 +116,11 @@ public class IotDeviceDO extends TenantBaseDO { */ private String ip; /** - * 设备的固件版本 + * 固件编号 + * + * 关联 {@link IotOtaFirmwareDO#getId()} */ - private String firmwareVersion; + private String firmwareId; /** * 设备密钥,用于设备认证,需安全存储 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java index 14924a7866..52f6d83759 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.ota; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -57,6 +58,12 @@ public class IotOtaUpgradeRecordDO extends BaseDO { * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} */ private String deviceId; + /** + * 来源的固件编号 + * + * 关联 {@link IotDeviceDO#getFirmwareId()} + */ + private Long fromFirmwareId; /** * 升级状态 From 795e06bc8fc1ae36d93e8e2e9dd41be7f4c019e7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 7 Feb 2025 21:06:03 +0800 Subject: [PATCH 159/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91IoT=EF=BC=9A=E6=8F=92=E4=BB=B6=E8=BF=98?= =?UTF-8?q?=E6=98=AF=E8=80=83=E8=99=91=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=EF=BC=8C=E5=9B=A0=E6=AD=A4=E9=9C=80=E8=A6=81=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E9=83=A8=E5=88=86=E5=9C=BA=E6=99=AF=E4=B8=8B=E7=9A=84?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=EF=BC=8C=E9=81=BF=E5=85=8D=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/plugin/IotPluginConfigDO.java | 5 ++--- .../dal/dataobject/plugin/IotPluginInstanceDO.java | 4 ++-- .../iot/job/plugin/IotPluginInstancesJob.java | 2 ++ .../plugin/IotPluginInstanceServiceImpl.java | 13 +++++++++---- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java index d0695f8963..69b7383910 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java @@ -1,9 +1,8 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugin; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum; -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum; import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -23,7 +22,7 @@ import lombok.*; @Builder @NoArgsConstructor @AllArgsConstructor -public class IotPluginConfigDO extends BaseDO { +public class IotPluginConfigDO extends TenantBaseDO { /** * 主键 ID diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java index 71d741b052..c64fe86c60 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.plugin; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -21,7 +21,7 @@ import java.time.LocalDateTime; @Builder @NoArgsConstructor @AllArgsConstructor -public class IotPluginInstanceDO extends BaseDO { +public class IotPluginInstanceDO extends TenantBaseDO { /** * 主键 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java index 261af3e58a..1d24417377 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.job.plugin; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; import org.springframework.stereotype.Component; @@ -28,6 +29,7 @@ public class IotPluginInstancesJob implements JobHandler { private IotPluginInstanceService pluginInstanceService; @Override + @TenantJob public String execute(String param) { int count = pluginInstanceService.offlineTimeoutPluginInstance( LocalDateTime.now().minus(OFFLINE_TIMEOUT)); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java index a3edbc1757..8c9973a703 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.plugin; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; @@ -62,7 +63,8 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { @Override public void heartbeatPluginInstance(IotPluginInstanceHeartbeatReqDTO heartbeatReqDTO) { // 情况一:已存在,则进行更新 - IotPluginInstanceDO instance = pluginInstanceMapper.selectByProcessId(heartbeatReqDTO.getProcessId()); + IotPluginInstanceDO instance = TenantUtils.executeIgnore( + () -> pluginInstanceMapper.selectByProcessId(heartbeatReqDTO.getProcessId())); if (instance != null) { IotPluginInstanceDO.IotPluginInstanceDOBuilder updateObj = IotPluginInstanceDO.builder().id(instance.getId()) .hostIp(heartbeatReqDTO.getHostIp()).downstreamPort(heartbeatReqDTO.getDownstreamPort()) @@ -74,12 +76,14 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { } else { updateObj.offlineTime(LocalDateTime.now()); } - pluginInstanceMapper.updateById(updateObj.build()); + TenantUtils.execute(instance.getTenantId(), + () -> pluginInstanceMapper.updateById(updateObj.build())); return; } // 情况二:不存在,则创建 - IotPluginConfigDO info = pluginConfigService.getPluginConfigByPluginKey(heartbeatReqDTO.getPluginKey()); + IotPluginConfigDO info = TenantUtils.executeIgnore( + () -> pluginConfigService.getPluginConfigByPluginKey(heartbeatReqDTO.getPluginKey())); if (info == null) { log.error("[heartbeatPluginInstance][心跳({}) 对应的插件不存在]", heartbeatReqDTO); return; @@ -93,7 +97,8 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { } else { insertObj.offlineTime(LocalDateTime.now()); } - pluginInstanceMapper.insert(insertObj.build()); + TenantUtils.execute(info.getTenantId(), + () -> pluginInstanceMapper.insert(insertObj.build())); } @Override From 4919439b960f09f03b836d3d4d7b2a07b892b695 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 7 Feb 2025 21:18:57 +0800 Subject: [PATCH 160/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9AOTA=20=E5=8D=87=E7=BA=A7=E7=9A=84?= =?UTF-8?q?=E4=B8=8B=E8=A1=8C=E6=B6=88=E6=81=AF=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../downstream/IotDeviceOtaUpgradeReqDTO.java | 66 ++++++++++++++++ .../upstream/IotDeviceOtaProgressReqDTO.java | 35 +++++++++ .../upstream/IotDeviceOtaPullReqDTO.java | 21 +++++ .../upstream/IotDeviceOtaReportReqDTO.java | 21 +++++ .../IotDeviceMessageIdentifierEnum.java | 7 +- .../device/IotDeviceMessageTypeEnum.java | 3 +- .../admin/device/IotDeviceController.http | 21 +++++ .../IotDeviceDownstreamServiceImpl.java | 78 +++++++++++++++---- .../IotDeviceDownstreamHandler.java | 13 +++- .../downstream/IotDeviceDownstreamServer.java | 7 +- .../IotDeviceOtaUpgradeVertxHandler.java | 60 ++++++++++++++ .../IotDeviceDownstreamHandlerImpl.java | 10 ++- 12 files changed, 311 insertions(+), 31 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java new file mode 100644 index 0000000000..8eccec42ec --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; + +import cn.hutool.core.map.MapUtil; +import lombok.Data; + +import java.util.Map; + +/** + * IoT 设备【OTA】升级下发 Request DTO(更新固件消息) + * + * @author 芋道源码 + */ +@Data +public class IotDeviceOtaUpgradeReqDTO extends IotDeviceDownstreamAbstractReqDTO { + + /** + * 固件编号 + */ + private Long firmwareId; + /** + * 固件版本 + */ + private String version; + + /** + * 签名方式 + * + * 例如说:MD5、SHA256 + */ + private String signMethod; + /** + * 固件文件签名 + */ + private String fileSign; + /** + * 固件文件大小 + */ + private Long fileSize; + /** + * 固件文件 URL + */ + private String fileUrl; + + /** + * 自定义信息,建议使用 JSON 格式 + */ + private String information; + + public static IotDeviceOtaUpgradeReqDTO build(Map map) { + return new IotDeviceOtaUpgradeReqDTO() + .setFirmwareId(MapUtil.getLong(map, "firmwareId")).setVersion((String) map.get("version")) + .setSignMethod((String) map.get("signMethod")).setFileSign((String) map.get("fileSign")) + .setFileSize(MapUtil.getLong(map, "fileSize")).setFileUrl((String) map.get("fileUrl")) + .setInformation((String) map.get("information")); + } + + public static Map build(IotDeviceOtaUpgradeReqDTO dto) { + return MapUtil.builder() + .put("firmwareId", dto.getFirmwareId()).put("version", dto.getVersion()) + .put("signMethod", dto.getSignMethod()).put("fileSign", dto.getFileSign()) + .put("fileSize", dto.getFileSize()).put("fileUrl", dto.getFileUrl()) + .put("information", dto.getInformation()) + .build(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java new file mode 100644 index 0000000000..a88a72e919 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import lombok.Data; + +// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/progress +/** + * IoT 设备【OTA】升级进度 Request DTO(上报更新固件进度) + * + * @author 芋道源码 + */ +@Data +public class IotDeviceOtaProgressReqDTO extends IotDeviceUpstreamAbstractReqDTO { + + /** + * 固件编号 + */ + private Long firmwareId; + + /** + * 升级状态 + * + * 枚举 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} + */ + private Integer status; + /** + * 升级进度,百分比 + */ + private Integer progress; + + /** + * 升级进度描述 + */ + private String description; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java new file mode 100644 index 0000000000..6328704e58 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/pull +/** + * IoT 设备【OTA】升级下拉 Request DTO(拉取固件更新) + * + * @author 芋道源码 + */ +public class IotDeviceOtaPullReqDTO { + + /** + * 固件编号 + */ + private Long firmwareId; + + /** + * 固件版本 + */ + private String version; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java new file mode 100644 index 0000000000..2b3b91c985 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/report +/** + * IoT 设备【OTA】上报 Request DTO(上报固件版本) + * + * @author 芋道源码 + */ +public class IotDeviceOtaReportReqDTO { + + /** + * 固件编号 + */ + private Long firmwareId; + + /** + * 固件版本 + */ + private String version; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index 8f6270c7b9..f8eca51eda 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -21,7 +21,12 @@ public enum IotDeviceMessageIdentifierEnum { CONFIG_SET("set"), // 下行 SERVICE_INVOKE("${identifier}"), // 下行 - SERVICE_REPLY_SUFFIX("_reply"); // 芋艿:TODO 芋艿:【讨论】上行 or 下行 + SERVICE_REPLY_SUFFIX("_reply"), // 芋艿:TODO 芋艿:【讨论】上行 or 下行 + + OTA_UPGRADE("upgrade"), // 下行 + OTA_PULL("pull"), // 上行 + OTA_PROGRESS("progress"), // 上行 + OTA_REPORT("report"),; // 上行 /** * 标志符 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java index 45b7596ced..2ee0a8acc0 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -17,7 +17,8 @@ public enum IotDeviceMessageTypeEnum implements ArrayValuable { PROPERTY("property"), // 设备属性 EVENT("event"), // 设备事件 SERVICE("service"), // 设备服务 - CONFIG("config"); // 设备配置 + CONFIG("config"), // 设备配置 + OTA("ota"),; // 设备 OTA public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http index 042ad7e7f2..c1190cec16 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.http @@ -51,4 +51,25 @@ Authorization: Bearer {{token}} "id": 25, "type": "config", "identifier": "set" +} + +### 请求 /iot/device/downstream 接口(OTA 升级) => 成功 +POST {{baseUrl}}/iot/device/downstream +Content-Type: application/json +tenant-id: {{adminTenentId}} +Authorization: Bearer {{token}} + +{ + "id": 25, + "type": "ota", + "identifier": "upgrade", + "data": { + "firmwareId": 1, + "version": "1.0.0", + "signMethod": "MD5", + "fileSign": "d41d8cd98f00b204e9800998ecf8427e", + "fileSize": 1024, + "fileUrl": "http://example.com/firmware.bin", + "information": "{\"desc\":\"升级到最新版本\"}" + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index bac2ef6c90..7c1ec5bb8b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -80,11 +80,15 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic } // 配置下发 if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.CONFIG.getType()) - && Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) { + && Objects.equals(downstreamReqVO.getIdentifier(), + IotDeviceMessageIdentifierEnum.CONFIG_SET.getIdentifier())) { return setDeviceConfig(downstreamReqVO, device, parentDevice); } - // TODO 芋艿:ota 升级 - return null; + // OTA 升级 + if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) { + return otaUpgrade(downstreamReqVO, device, parentDevice); + } + throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO); } /** @@ -97,7 +101,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic */ @SuppressWarnings("unchecked") private IotDeviceMessage invokeDeviceService(IotDeviceDownstreamReqVO downstreamReqVO, - IotDeviceDO device, IotDeviceDO parentDevice) { + IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数校验 if (!(downstreamReqVO.getData() instanceof Map)) { throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); @@ -105,9 +109,10 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic // TODO @super:【可优化】过滤掉不合法的服务 // 2. 发送请求 - String url = String.format( "sys/%s/%s/thing/service/%s", - getProductKey(device, parentDevice), getDeviceName(device, parentDevice), downstreamReqVO.getIdentifier()); - IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO() + String url = String.format("sys/%s/%s/thing/service/%s", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice), + downstreamReqVO.getIdentifier()); + IotDeviceServiceInvokeReqDTO reqDTO = new IotDeviceServiceInvokeReqDTO() .setParams((Map) downstreamReqVO.getData()); CommonResult result = requestPlugin(url, reqDTO, device); @@ -144,7 +149,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic // TODO @super:【可优化】过滤掉不合法的属性 // 2. 发送请求 - String url = String.format( "sys/%s/%s/thing/service/property/set", + String url = String.format("sys/%s/%s/thing/service/property/set", getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); IotDevicePropertySetReqDTO reqDTO = new IotDevicePropertySetReqDTO() .setProperties((Map) downstreamReqVO.getData()); @@ -184,7 +189,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic // TODO @super:【可优化】过滤掉不合法的属性 // 2. 发送请求 - String url = String.format( "sys/%s/%s/thing/service/property/get", + String url = String.format("sys/%s/%s/thing/service/property/get", getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); IotDevicePropertyGetReqDTO reqDTO = new IotDevicePropertyGetReqDTO() .setIdentifiers((List) downstreamReqVO.getData()); @@ -214,14 +219,14 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic * @param parentDevice 父设备 * @return 下发消息 */ - @SuppressWarnings({"unchecked", "unused"}) + @SuppressWarnings({ "unchecked", "unused" }) private IotDeviceMessage setDeviceConfig(IotDeviceDownstreamReqVO downstreamReqVO, IotDeviceDO device, IotDeviceDO parentDevice) { // 1. 参数转换,无需校验 Map config = JsonUtils.parseObject(device.getConfig(), Map.class); // 2. 发送请求 - String url = String.format( "sys/%s/%s/thing/service/config/set", + String url = String.format("sys/%s/%s/thing/service/config/set", getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); IotDeviceConfigSetReqDTO reqDTO = new IotDeviceConfigSetReqDTO() .setConfig(config); @@ -243,16 +248,54 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic return message; } + /** + * 设备 OTA 升级 + * + * @param downstreamReqVO 下行请求 + * @param device 设备 + * @param parentDevice 父设备 + * @return 下发消息 + */ + private IotDeviceMessage otaUpgrade(IotDeviceDownstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // 1. 参数校验 + if (!(downstreamReqVO.getData() instanceof Map data)) { + throw new ServiceException(BAD_REQUEST.getCode(), "data 不是 Map 类型"); + } + + // 2. 发送请求 + String url = String.format("ota/%s/%s/upgrade", + getProductKey(device, parentDevice), getDeviceName(device, parentDevice)); + IotDeviceOtaUpgradeReqDTO reqDTO = IotDeviceOtaUpgradeReqDTO.build(data); + CommonResult result = requestPlugin(url, reqDTO, device); + + // 3. 发送设备消息 + IotDeviceMessage message = new IotDeviceMessage().setRequestId(reqDTO.getRequestId()) + .setType(IotDeviceMessageTypeEnum.OTA.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.OTA_UPGRADE.getIdentifier()) + .setData(downstreamReqVO.getData()); + sendDeviceMessage(message, device, result.getCode()); + + // 4. 如果不成功,抛出异常,提示用户 + if (result.isError()) { + log.error("[otaUpgrade][设备({}) OTA 升级失败,请求参数:({}),响应结果:({})]", + device.getDeviceKey(), reqDTO, result); + throw exception(DEVICE_DOWNSTREAM_FAILED, result.getMsg()); + } + return message; + } + /** * 请求插件 * - * @param url URL - * @param reqDTO 请求参数,只需要设置子类的参数! - * @param device 设备 + * @param url URL + * @param reqDTO 请求参数,只需要设置子类的参数! + * @param device 设备 * @return 响应结果 */ - @SuppressWarnings({"unchecked", "HttpUrlsUsage"}) - private CommonResult requestPlugin(String url, IotDeviceDownstreamAbstractReqDTO reqDTO, IotDeviceDO device) { + @SuppressWarnings({ "unchecked", "HttpUrlsUsage" }) + private CommonResult requestPlugin(String url, IotDeviceDownstreamAbstractReqDTO reqDTO, + IotDeviceDO device) { // 获得设备对应的插件实例 IotPluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstanceByDeviceKey(device.getDeviceKey()); if (pluginInstance == null) { @@ -266,7 +309,8 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic ResponseEntity> responseEntity; try { responseEntity = restTemplate.postForEntity( - String.format("http://%s:%d/%s", pluginInstance.getHostIp(), pluginInstance.getDownstreamPort(), url), + String.format("http://%s:%d/%s", pluginInstance.getHostIp(), pluginInstance.getDownstreamPort(), + url), reqDTO, (Class>) (Class) CommonResult.class); Assert.isTrue(responseEntity.getStatusCode().is2xxSuccessful(), "HTTP 状态码不是 2xx,而是" + responseEntity.getStatusCode()); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java index 62d72785d9..38aba3df66 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamHandler.java @@ -1,10 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceConfigSetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; /** * IoT 设备下行处理器 @@ -47,4 +44,12 @@ public interface IotDeviceDownstreamHandler { */ CommonResult setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO); + /** + * 升级设备 OTA + * + * @param upgradeReqDTO 升级设备 OTA 的请求 + * @return 是否成功 + */ + CommonResult upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java index 6b08dba009..719fdb5c3f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/IotDeviceDownstreamServer.java @@ -1,10 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream; import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; -import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceConfigSetVertxHandler; -import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertyGetVertxHandler; -import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDevicePropertySetVertxHandler; -import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.IotDeviceServiceInvokeVertxHandler; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.router.*; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.ext.web.Router; @@ -39,6 +36,8 @@ public class IotDeviceDownstreamServer { .handler(new IotDevicePropertyGetVertxHandler(deviceDownstreamHandler)); router.post(IotDeviceConfigSetVertxHandler.PATH) .handler(new IotDeviceConfigSetVertxHandler(deviceDownstreamHandler)); + router.post(IotDeviceOtaUpgradeVertxHandler.PATH) + .handler(new IotDeviceOtaUpgradeVertxHandler(deviceDownstreamHandler)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java new file mode 100644 index 0000000000..f81d385dc6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceOtaUpgradeReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import io.vertx.core.json.JsonObject; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +@Slf4j +@RequiredArgsConstructor +public class IotDeviceOtaUpgradeVertxHandler implements Handler { + + public static final String PATH = "/ota/:productKey/:deviceName/upgrade"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + public void handle(RoutingContext routingContext) { + // 1. 解析参数 + IotDeviceOtaUpgradeReqDTO reqDTO; + try { + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + String requestId = body.getString("requestId"); + Long firmwareId = body.getLong("firmwareId"); + String version = body.getString("version"); + String signMethod = body.getString("signMethod"); + String fileSign = body.getString("fileSign"); + Long fileSize = body.getLong("fileSize"); + String fileUrl = body.getString("fileUrl"); + String information = body.getString("information"); + reqDTO = ((IotDeviceOtaUpgradeReqDTO) new IotDeviceOtaUpgradeReqDTO() + .setRequestId(requestId).setProductKey(productKey).setDeviceName(deviceName)) + .setFirmwareId(firmwareId).setVersion(version) + .setSignMethod(signMethod).setFileSign(fileSign).setFileSize(fileSize).setFileUrl(fileUrl) + .setInformation(information); + } catch (Exception e) { + log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + return; + } + + // 2. 调用处理器 + try { + CommonResult result = deviceDownstreamHandler.upgradeDeviceOta(reqDTO); + IotPluginCommonUtils.writeJson(routingContext, result); + } catch (Exception e) { + log.error("[handle][请求参数({}) OTA 升级异常]", reqDTO, e); + IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java index 3c86ad6d26..869fe72345 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/downstream/IotDeviceDownstreamHandlerImpl.java @@ -1,10 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.downstream; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceConfigSetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED; @@ -39,4 +36,9 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); } + @Override + public CommonResult upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { + return CommonResult.error(NOT_IMPLEMENTED.getCode(), "HTTP 不支持设置设备属性"); + } + } From 724512399a5f434f9fa5272b37800a361259b1b4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 8 Feb 2025 07:29:17 +0800 Subject: [PATCH 161/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E5=8A=A8=E6=80=81=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=20js=E3=80=81groovy=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 21 +++++++ .../iocoder/yudao/module/iot/ScriptTest.java | 61 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/ScriptTest.java diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 805205a167..6f01e33191 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -81,6 +81,27 @@ pf4j-spring + + + org.apache.groovy + groovy-all + 4.0.25 + pom + + + + + org.graalvm.js + js + 24.1.2 + pom + + + org.graalvm.js + js-scriptengine + 24.1.2 + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/ScriptTest.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/ScriptTest.java new file mode 100644 index 0000000000..9f54d60e80 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/ScriptTest.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.iot; + +import cn.hutool.script.ScriptUtil; +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptException; + +/** + * TODO 芋艿:测试脚本的接入 + */ +public class ScriptTest { + + public static void main2(String[] args) { + // 创建一个 Groovy 脚本引擎 + ScriptEngine engine = ScriptUtil.createGroovyEngine(); + + // 创建绑定参数 + Bindings bindings = engine.createBindings(); + bindings.put("name", "Alice"); + bindings.put("age", 30); + + // 定义一个稍微复杂的 Groovy 脚本 + String script = "def greeting = 'Hello, ' + name + '!';\n" + + "def ageInFiveYears = age + 5;\n" + + "def message = greeting + ' In five years, you will be ' + ageInFiveYears + ' years old.';\n" + + "return message.toUpperCase();\n"; + + try { + // 执行脚本并获取结果 + Object result = engine.eval(script, bindings); + System.out.println(result); // 输出: HELLO, ALICE! IN FIVE YEARS, YOU WILL BE 35 YEARS OLD. + } catch (ScriptException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + // 创建一个 JavaScript 脚本引擎 + ScriptEngine jsEngine = ScriptUtil.createJsEngine(); + + // 创建绑定参数 + Bindings jsBindings = jsEngine.createBindings(); + jsBindings.put("name", "Bob"); + jsBindings.put("age", 25); + + // 定义一个简单的 JavaScript 脚本 + String jsScript = "var greeting = 'Hello, ' + name + '!';\n" + + "var ageInTenYears = age + 10;\n" + + "var message = greeting + ' In ten years, you will be ' + ageInTenYears + ' years old.';\n" + + "message.toUpperCase();\n"; + + try { + // 执行脚本并获取结果 + Object jsResult = jsEngine.eval(jsScript, jsBindings); + System.out.println(jsResult); // 输出: HELLO, BOB! IN TEN YEARS, YOU WILL BE 35 YEARS OLD. + } catch (ScriptException e) { + e.printStackTrace(); + } + } + +} From d718f801084f9c92ce69b695e059a53a6adb04a5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 8 Feb 2025 07:39:48 +0800 Subject: [PATCH 162/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9Aplugin=20=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml | 1 + .../module/iot/framework/plugin/core/IotPluginStartRunner.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 2a14c88b87..95ab6f7a4e 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -63,6 +63,7 @@ opengauss-jdbc true + com.taosdata.jdbc taos-jdbcdriver diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java index 8036eb2d47..64d258514e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java @@ -30,7 +30,6 @@ public class IotPluginStartRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { List pluginConfigList = TenantUtils.executeIgnore( - // 查询运行中且部署类型为 JAR 的插件 () -> pluginConfigService.getPluginConfigListByStatusAndDeployType( IotPluginStatusEnum.RUNNING.getStatus(), IotPluginDeployTypeEnum.JAR.getDeployType())); if (CollUtil.isEmpty(pluginConfigList)) { From 6abd67a38c671219f138a52a50d4d4ceda632af3 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 8 Feb 2025 17:31:11 +0800 Subject: [PATCH 163/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E7=89=A9=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thingmodel/IotDataSpecsDataTypeEnum.java | 13 +++++- .../IotThingModelAccessModeEnum.java | 13 +++++- .../IotThingModelParamDirectionEnum.java | 14 +++++- .../IotThingModelServiceCallTypeEnum.java | 13 +++++- .../IotThingModelServiceEventTypeEnum.java | 13 +++++- .../thingmodel/model/ThingModelEvent.java | 11 ++++- .../thingmodel/model/ThingModelParam.java | 14 +++++- .../thingmodel/model/ThingModelProperty.java | 16 +++++-- .../thingmodel/model/ThingModelService.java | 12 +++++- .../thingmodel/vo/IotThingModelListReqVO.java | 7 ++- .../thingmodel/vo/IotThingModelPageReqVO.java | 7 ++- .../thingmodel/vo/IotThingModelRespVO.java | 13 +++--- .../thingmodel/vo/IotThingModelSaveReqVO.java | 15 ++++--- .../thingmodel/IotThingModelServiceImpl.java | 43 +++++++++++-------- 14 files changed, 148 insertions(+), 56 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java index aa455f8cce..5524fdeb4a 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotDataSpecsDataTypeEnum.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; -// TODO @puhui999:加个 ArrayValuable +import java.util.Arrays; + /** * IoT 数据定义的数据类型枚举类 * @@ -11,7 +13,7 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotDataSpecsDataTypeEnum { +public enum IotDataSpecsDataTypeEnum implements ArrayValuable { INT("int"), FLOAT("float"), @@ -23,6 +25,13 @@ public enum IotDataSpecsDataTypeEnum { STRUCT("struct"), ARRAY("array"); + public static final String[] ARRAYS = Arrays.stream(values()).map(IotDataSpecsDataTypeEnum::getDataType).toArray(String[]::new); + private final String dataType; + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java index 6eb6ddb7e5..a78614853f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; -// TODO @puhui999:加个 ArrayValuable +import java.util.Arrays; + /** * IOT 产品物模型属性读取类型枚举 * @@ -11,11 +13,18 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotThingModelAccessModeEnum { +public enum IotThingModelAccessModeEnum implements ArrayValuable { READ_ONLY("r"), READ_WRITE("rw"); + public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelAccessModeEnum::getMode).toArray(String[]::new); + private final String mode; + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java index 1e875cd234..00158a0f9b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; -// TODO @puhui999:加个 ArrayValuable +import java.util.Arrays; + + /** * IOT 产品物模型参数是输入参数还是输出参数枚举 * @@ -11,11 +14,18 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotThingModelParamDirectionEnum { +public enum IotThingModelParamDirectionEnum implements ArrayValuable { INPUT("input"), // 输入参数 OUTPUT("output"); // 输出参数 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelParamDirectionEnum::getDirection).toArray(String[]::new); + private final String direction; + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java index d547bf1041..d6ed70e502 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; -// TODO @puhui999:加个 ArrayValuable +import java.util.Arrays; + /** * IOT 产品物模型服务调用方式枚举 * @@ -11,11 +13,18 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotThingModelServiceCallTypeEnum { +public enum IotThingModelServiceCallTypeEnum implements ArrayValuable { ASYNC("async"), // 异步调用 SYNC("sync"); // 同步调用 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelServiceCallTypeEnum::getType).toArray(String[]::new); + private final String type; + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java index 89271d7fca..584c0743fd 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.module.iot.enums.thingmodel; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; -// TODO @puhui999:加个 ArrayValuable +import java.util.Arrays; + /** * IOT 产品物模型事件类型枚举 * @@ -11,12 +13,19 @@ import lombok.Getter; */ @AllArgsConstructor @Getter -public enum IotThingModelServiceEventTypeEnum { +public enum IotThingModelServiceEventTypeEnum implements ArrayValuable { INFO("info"), // 信息 ALERT("alert"), // 告警 ERROR("error"); // 故障 + public static final String[] ARRAYS = Arrays.stream(values()).map(IotThingModelServiceEventTypeEnum::getType).toArray(String[]::new); + private final String type; + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 0a5e0056f0..151d1f571e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -1,11 +1,14 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceEventTypeEnum; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; import lombok.Data; import java.util.List; -// TODO @puhui999:必要的参数校验 /** * 物模型中的事件 * @@ -17,10 +20,13 @@ public class ThingModelEvent { /** * 事件标识符 */ + @NotEmpty(message = "事件标识符不能为空") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "事件标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") private String identifier; /** * 事件名称 */ + @NotEmpty(message = "事件名称不能为空") private String name; /** * 是否是标准品类的必选事件 @@ -31,12 +37,15 @@ public class ThingModelEvent { * * 枚举 {@link IotThingModelServiceEventTypeEnum} */ + @NotEmpty(message = "事件类型不能为空") + @InEnum(IotThingModelServiceEventTypeEnum.class) private String type; /** * 事件的输出参数 * * 输出参数定义事件调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 */ + @Valid private List outputParams; /** * 标识设备需要执行的具体操作 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java index 6215d1537c..dc1e7deac6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -1,12 +1,15 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelParamDirectionEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; import lombok.Data; import java.util.List; -// TODO @puhui999:必要的参数校验 /** * IOT 产品物模型中的参数 * @@ -18,16 +21,21 @@ public class ThingModelParam { /** * 参数标识符 */ + @NotEmpty(message = "参数标识符不能为空") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "参数标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") private String identifier; /** * 参数名称 */ + @NotEmpty(message = "参数名称不能为空") private String name; /** * 用于区分输入或输出参数 * * 枚举 {@link IotThingModelParamDirectionEnum} */ + @NotEmpty(message = "参数方向不能为空") + @InEnum(IotThingModelParamDirectionEnum.class) private String direction; /** * 参数的序号。从 0 开始排序,且不能重复。 @@ -37,7 +45,11 @@ public class ThingModelParam { private Integer paraOrder; /** * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 + * + * 枚举 {@link IotDataSpecsDataTypeEnum} */ + @NotEmpty(message = "数据类型不能为空") + @InEnum(IotDataSpecsDataTypeEnum.class) private String dataType; /** * 参数值的数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 157a4c4889..866f25b84e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -1,12 +1,15 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.dataType.ThingModelDataSpecs; +import cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelAccessModeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; import lombok.Data; import java.util.List; -// TODO @puhui999:必要的参数校验 /** * 物模型中的属性 * @@ -20,26 +23,33 @@ public class ThingModelProperty { /** * 属性标识符 */ + @NotEmpty(message = "属性标识符不能为空") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") private String identifier; /** * 属性名称 */ + @NotEmpty(message = "属性名称不能为空") private String name; /** * 云端可以对该属性进行的操作类型 * * 枚举 {@link IotThingModelAccessModeEnum} */ + @NotEmpty(message = "操作类型不能为空") + @InEnum(IotThingModelAccessModeEnum.class) private String accessMode; /** * 是否是标准品类的必选服务 */ private Boolean required; /** - * 数据类型,与 dataSpecs 的 dataType 保持一致 + * 参数值的数据类型,与 dataSpecs 的 dataType 保持一致 * - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.thingmodel.IotDataSpecsDataTypeEnum} + * 枚举 {@link IotDataSpecsDataTypeEnum} */ + @NotEmpty(message = "数据类型不能为空") + @InEnum(IotDataSpecsDataTypeEnum.class) private String dataType; /** * 数据类型(dataType)为非列表型(int、float、double、text、date、array)的数据规范存储在 dataSpecs 中 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 067ca2ea1e..7db2b5f7a3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -1,11 +1,14 @@ package cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelServiceCallTypeEnum; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; import lombok.Data; import java.util.List; -// TODO @puhui999:必要的参数校验 /** * 物模型中的服务 * @@ -17,10 +20,13 @@ public class ThingModelService { /** * 服务标识符 */ + @NotEmpty(message = "服务标识符不能为空") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "服务标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") private String identifier; /** * 服务名称 */ + @NotEmpty(message = "服务名称不能为空") private String name; /** * 是否是标准品类的必选服务 @@ -31,18 +37,22 @@ public class ThingModelService { * * 枚举 {@link IotThingModelServiceCallTypeEnum} */ + @NotEmpty(message = "调用类型不能为空") + @InEnum(IotThingModelServiceCallTypeEnum.class) private String callType; /** * 服务的输入参数 * * 输入参数定义服务调用时所需提供的信息,用于控制设备行为或执行特定任务 */ + @Valid private List inputParams; /** * 服务的输出参数 * * 输出参数定义服务调用后返回的结果或反馈信息,用于确认操作结果或提供额外的信息。 */ + @Valid private List outputParams; /** * 标识设备需要执行的具体操作 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java index 8f7f374dd0..5b92256bb4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelListReqVO.java @@ -6,19 +6,18 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; -// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型 List Request VO") @Data public class IotThingModelListReqVO { - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "产品编号不能为空") private Long productId; - @Schema(description = "功能标识") + @Schema(description = "功能标识", example = "temperature") private String identifier; - @Schema(description = "功能名称", example = "张三") + @Schema(description = "功能名称", example = "温度") private String name; @Schema(description = "功能类型", example = "1") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java index 404bc7cc24..447eb6e9ae 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java @@ -9,21 +9,20 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class IotThingModelPageReqVO extends PageParam { - @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "产品编号不能为空") private Long productId; - @Schema(description = "功能标识") + @Schema(description = "功能标识", example = "temperature") private String identifier; - @Schema(description = "功能名称", example = "张三") + @Schema(description = "功能名称", example = "温度") private String name; @Schema(description = "功能类型", example = "1") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java index 37a42edc9e..72565a8c45 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java @@ -10,7 +10,6 @@ import lombok.Data; import java.time.LocalDateTime; -// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型 Response VO") @Data @ExcelIgnoreUnannotated @@ -20,23 +19,23 @@ public class IotThingModelRespVO { @ExcelProperty("产品ID") private Long id; - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long productId; - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_sensor") @ExcelProperty("产品标识") private String productKey; - @Schema(description = "功能标识", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature") private String identifier; - @Schema(description = "功能名称", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温度") private String name; - @Schema(description = "功能描述", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "测量当前环境温度") private String description; - @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java index 18b8f40429..1e8564df47 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelSaveReqVO.java @@ -6,11 +6,11 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelP import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.model.ThingModelService; import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; -// TODO @puhui999:部分字段,可以用 cursor 加上 example @Schema(description = "管理后台 - IoT 产品物模型新增/修改 Request VO") @Data public class IotThingModelSaveReqVO { @@ -22,33 +22,36 @@ public class IotThingModelSaveReqVO { @NotNull(message = "产品ID不能为空") private Long productId; - @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temperature_001") @NotEmpty(message = "产品标识不能为空") private String productKey; - @Schema(description = "功能标识", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "temp_monitor") @NotEmpty(message = "功能标识不能为空") private String identifier; - @Schema(description = "功能名称", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温度监测器") @NotEmpty(message = "功能名称不能为空") private String name; - @Schema(description = "功能描述", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "用于监测环境温度的传感器") private String description; - @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "功能类型不能为空") @InEnum(IotThingModelTypeEnum.class) private Integer type; @Schema(description = "属性", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid private ThingModelProperty property; @Schema(description = "服务", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid private ThingModelService service; @Schema(description = "事件", requiredMode = Schema.RequiredMode.REQUIRED) + @Valid private ThingModelEvent event; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index c57ab76ba7..b9167e4ef3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -55,7 +55,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { @Transactional(rollbackFor = Exception.class) public Long createThingModel(IotThingModelSaveReqVO createReqVO) { // 1.1 校验功能标识符在同一产品下是否唯一 - validateIdentifierUnique(createReqVO.getProductId(), createReqVO.getIdentifier()); + validateIdentifierUnique(null, createReqVO.getProductId(), createReqVO.getIdentifier()); // 1.2 功能名称在同一产品下是否唯一 validateNameUnique(createReqVO.getProductId(), createReqVO.getName()); // 1.3 校验产品状态,发布状态下,不允许新增功能 @@ -81,7 +81,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { // 1.1 校验功能是否存在 validateProductThingModelMapperExists(updateReqVO.getId()); // 1.2 校验功能标识符是否唯一 - validateIdentifierUniqueForUpdate(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); + validateIdentifierUnique(updateReqVO.getId(), updateReqVO.getProductId(), updateReqVO.getIdentifier()); // 1.3 校验产品状态,发布状态下,不允许操作功能 validateProductStatus(updateReqVO.getProductId()); @@ -159,8 +159,23 @@ public class IotThingModelServiceImpl implements IotThingModelService { } } - // TODO @puhui999:这个方法,和 validateIdentifierUnique 可以融合下 - private void validateIdentifierUniqueForUpdate(Long id, Long productId, String identifier) { + private void validateIdentifierUnique(Long id, Long productId, String identifier) { + // 1.0 情况一:创建时校验 + if (id == null) { + // 1.1 系统保留字段,不能用于标识符定义 + if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { + throw exception(THING_MODEL_IDENTIFIER_INVALID); + } + + // 1.2 校验唯一 + IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); + if (thingModel != null) { + throw exception(THING_MODEL_IDENTIFIER_EXISTS); + } + return; + } + + // 2.0 情况二:更新时校验 IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); if (thingModel != null && ObjectUtil.notEqual(thingModel.getId(), id)) { throw exception(THING_MODEL_IDENTIFIER_EXISTS); @@ -181,23 +196,10 @@ public class IotThingModelServiceImpl implements IotThingModelService { } } - private void validateIdentifierUnique(Long productId, String identifier) { - // 系统保留字段,不能用于标识符定义 - if (StrUtil.equalsAny(identifier, "set", "get", "post", "property", "event", "time", "value")) { - throw exception(THING_MODEL_IDENTIFIER_INVALID); - } - - // 校验唯一 - IotThingModelDO thingModel = thingModelMapper.selectByProductIdAndIdentifier(productId, identifier); - if (thingModel != null) { - throw exception(THING_MODEL_IDENTIFIER_EXISTS); - } - } - /** * 创建默认的事件和服务 * - * @param productId 产品编号 + * @param productId 产品编号 * @param productKey 产品标识 */ public void createDefaultEventsAndServices(Long productId, String productKey) { @@ -282,6 +284,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { } // TODO @haohao:是不是不用生成这个?目前属性上报,是个批量接口 + /** * 生成属性上报事件 */ @@ -298,6 +301,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { } // TODO @haohao:是不是不用生成这个?目前属性上报,是个批量接口 + /** * 生成属性设置服务 */ @@ -352,7 +356,8 @@ public class IotThingModelServiceImpl implements IotThingModelService { } @CacheEvict(value = RedisKeyConstants.THING_MODEL_LIST, key = "#productKey") - public void deleteThingModelListCache0(String productKey) {} + public void deleteThingModelListCache0(String productKey) { + } private IotThingModelServiceImpl getSelf() { return SpringUtil.getBean(getClass()); From 5f7bb8041f984980e9b56b66947e60a6dc2c6152 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 8 Feb 2025 19:31:50 +0800 Subject: [PATCH 164/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=20register=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/api/device/IotDeviceUpstreamApi.java | 14 +++-- .../upstream/IotDeviceRegisterReqDTO.java | 12 ++++ .../IotDeviceMessageIdentifierEnum.java | 6 +- .../device/IotDeviceMessageTypeEnum.java | 11 ++-- .../api/device/IoTDeviceUpstreamApiImpl.java | 11 ++-- .../device/vo/device/IotDeviceSaveReqVO.java | 2 + .../dal/dataobject/product/IotProductDO.java | 4 +- .../iot/service/device/IotDeviceService.java | 13 +++- .../service/device/IotDeviceServiceImpl.java | 59 ++++++++++++++----- .../control/IotDeviceUpstreamService.java | 8 +++ .../control/IotDeviceUpstreamServiceImpl.java | 28 +++++++-- .../service/product/IotProductService.java | 8 +++ .../product/IotProductServiceImpl.java | 5 ++ 13 files changed, 146 insertions(+), 35 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index a6601d551b..a69ea180cc 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -1,10 +1,7 @@ package cn.iocoder.yudao.module.iot.api.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; import cn.iocoder.yudao.module.iot.enums.ApiConstants; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; @@ -47,6 +44,15 @@ public interface IotDeviceUpstreamApi { @PostMapping(PREFIX + "/report-event") CommonResult reportDeviceEvent(@Valid @RequestBody IotDeviceEventReportReqDTO reportReqDTO); + // TODO @芋艿:这个需要 plugins 接入下 + /** + * 注册设备 + * + * @param registerReqDTO 注册设备 DTO + */ + @PostMapping(PREFIX + "/register") + CommonResult registerDevice(@Valid @RequestBody IotDeviceRegisterReqDTO registerReqDTO); + // ========== 插件相关 ========== /** diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java new file mode 100644 index 0000000000..a627cba421 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import lombok.Data; + +/** + * IoT 设备【注册】注册 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDeviceRegisterReqDTO extends IotDeviceUpstreamAbstractReqDTO { +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index f8eca51eda..6dd10a3865 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -26,7 +26,11 @@ public enum IotDeviceMessageIdentifierEnum { OTA_UPGRADE("upgrade"), // 下行 OTA_PULL("pull"), // 上行 OTA_PROGRESS("progress"), // 上行 - OTA_REPORT("report"),; // 上行 + OTA_REPORT("report"), // 上行 + + REGISTER_REGISTER("register"), // 上行 + REGISTER_SUB_REGISTER("sub_register"), // 上行 + REGISTER_SUB_UNREGISTER("sub_unregister"),; // 下行 /** * 标志符 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java index 2ee0a8acc0..000af57d46 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -14,11 +14,12 @@ import java.util.Arrays; public enum IotDeviceMessageTypeEnum implements ArrayValuable { STATE("state"), // 设备状态 - PROPERTY("property"), // 设备属性 - EVENT("event"), // 设备事件 - SERVICE("service"), // 设备服务 - CONFIG("config"), // 设备配置 - OTA("ota"),; // 设备 OTA + PROPERTY("property"), // 设备属性:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务 + EVENT("event"), // 设备事件:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务 + SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务 + CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置 + OTA("ota"), // 设备 OTA:可参考 https://help.aliyun.com/zh/iot/user-guide/ota-update OTA 升级 + REGISTER("register"),; // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册 public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index 6a437dfca0..21b6c542a4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -1,10 +1,7 @@ package cn.iocoder.yudao.module.iot.api.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService; import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; import org.springframework.validation.annotation.Validated; @@ -46,6 +43,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { return success(true); } + @Override + public CommonResult registerDevice(IotDeviceRegisterReqDTO registerReqDTO) { + deviceUpstreamService.registerDevice(registerReqDTO); + return success(true); + } + // ========== 插件相关 ========== @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index 7d9ac6a0d0..5da2f501b6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.Size; import lombok.Data; @@ -18,6 +19,7 @@ public class IotDeviceSaveReqVO { private String deviceKey; @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "设备名称不能为空") private String deviceName; @Schema(description = "备注名称", example = "张三") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java index 34fd35706b..37c7be44be 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.product; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -19,7 +19,7 @@ import lombok.*; @Builder @NoArgsConstructor @AllArgsConstructor -public class IotProductDO extends BaseDO { +public class IotProductDO extends TenantBaseDO { /** * 产品 ID diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index f77ae3a375..9ff0e81c29 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; import javax.annotation.Nullable; import java.util.Collection; @@ -17,13 +18,23 @@ import java.util.List; public interface IotDeviceService { /** - * 创建设备 + * 【管理员】创建设备 * * @param createReqVO 创建信息 * @return 编号 */ Long createDevice(@Valid IotDeviceSaveReqVO createReqVO); + /** + * 【设备注册】创建设备 + * + * @param productKey 产品标识 + * @param deviceName 设备名称 + * @return 设备 + */ + IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey, + @NotEmpty(message = "设备名称不能为空") String deviceName); + /** * 更新设备 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 2680f47d13..6e20d724f9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -71,7 +71,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { } }); // 1.3 校验设备名称在同一产品下是否唯一 - if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) { + if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceName()) != null) { throw exception(DEVICE_NAME_EXISTS); } // 1.4 校验父设备是否为合法网关 @@ -82,23 +82,54 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 1.5 校验分组存在 deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); - // 2.1 转换 VO 为 DO - // TODO @芋艿:各种 mqtt 是不是可以简化! - IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> { - o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); - // 生成并设置必要的字段 - o.setDeviceSecret(generateDeviceSecret()) - .setMqttClientId(generateMqttClientId()) - .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey())) - .setMqttPassword(generateMqttPassword()); - // 设置设备状态为未激活 - o.setState(IotDeviceStateEnum.INACTIVE.getState()); - }); - // 2.2 插入到数据库 + // 2. 插入到数据库 + IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class); + initDevice(device, product); deviceMapper.insert(device); return device.getId(); } + @Override + public IotDeviceDO createDevice(String productKey, String deviceName) { + // 1.1 校验产品是否存在 + IotProductDO product = TenantUtils.executeIgnore(() -> + productService.getProductByProductKey(productKey)); + if (product == null) { + throw exception(PRODUCT_NOT_EXISTS); + } + // 1.2 校验设备标识是否唯一 + String deviceKey = generateDeviceKey(); + TenantUtils.executeIgnore(() -> { + if (deviceMapper.selectByDeviceKey(deviceKey) != null) { + throw exception(PRODUCT_KEY_EXISTS); + } + }); + return TenantUtils.execute(product.getTenantId(), () -> { + // 1.3 校验设备名称在同一产品下是否唯一 + if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), deviceName) != null) { + throw exception(DEVICE_NAME_EXISTS); + } + + // 2. 插入到数据库 + IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey); + initDevice(device, product); + deviceMapper.insert(device); + return device; + }); + } + + private void initDevice(IotDeviceDO device, IotProductDO product) { + device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); + // 生成并设置必要的字段 + // TODO @芋艿:各种 mqtt 是不是可以简化! + device.setDeviceSecret(generateDeviceSecret()) + .setMqttClientId(generateMqttClientId()) + .setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey())) + .setMqttPassword(generateMqttPassword()); + // 设置设备状态为未激活 + device.setState(IotDeviceStateEnum.INACTIVE.getState()); + } + @Override public void updateDevice(IotDeviceSaveReqVO updateReqVO) { updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index 8bc0ffd697..330444e345 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.service.device.control; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceRegisterReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import jakarta.validation.Valid; @@ -43,4 +44,11 @@ public interface IotDeviceUpstreamService { */ void reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO); + /** + * 注册设备 + * + * @param registerReqDTO 注册设备 DTO + */ + void registerDevice(IotDeviceRegisterReqDTO registerReqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index 1cbf3cd2a0..e3cd43bad0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -7,10 +7,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceUpstreamAbstractReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; @@ -165,6 +162,29 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { sendDeviceMessage(message, device); } + @Override + public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) { + // 1.1 注册设备 + log.info("[registerDevice][注册设备: {}]", registerReqDTO); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); + boolean register = device == null; + if (device == null) { + device = deviceService.createDevice(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); + log.info("[registerDevice][请求({}) 成功注册设备({})]", registerReqDTO, device); + } + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, registerReqDTO); + + // 2. 发送设备消息 + if (register) { + IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.REGISTER.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier()); + sendDeviceMessage(message, device); + } + } + private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { // 1. 【异步】记录设备与插件实例的映射 pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index 9a2e96f671..a317cacae9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -45,6 +45,14 @@ public interface IotProductService { */ IotProductDO getProduct(Long id); + /** + * 根据产品 key 获得产品 + * + * @param productKey 产品 key + * @return 产品 + */ + IotProductDO getProductByProductKey(String productKey); + /** * 校验产品存在 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 3fa0bcf0b1..fc2300b7cb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -105,6 +105,11 @@ public class IotProductServiceImpl implements IotProductService { return productMapper.selectById(id); } + @Override + public IotProductDO getProductByProductKey(String productKey) { + return productMapper.selectByProductKey(productKey); + } + @Override public PageResult getProductPage(IotProductPageReqVO pageReqVO) { return productMapper.selectPage(pageReqVO); From 4254c06c3764a8c8b25d4182ef8d50f03ef88bad Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 8 Feb 2025 20:56:16 +0800 Subject: [PATCH 165/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=20sub=20register=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/api/device/IotDeviceUpstreamApi.java | 9 +++ .../upstream/IotDeviceRegisterReqDTO.java | 2 +- .../upstream/IotDeviceRegisterSubReqDTO.java | 42 +++++++++++++ .../IotDeviceMessageIdentifierEnum.java | 4 +- .../product/IotProductDeviceTypeEnum.java | 10 +++ .../api/device/IoTDeviceUpstreamApiImpl.java | 6 ++ .../device/vo/device/IotDeviceSaveReqVO.java | 4 +- .../iot/service/device/IotDeviceService.java | 15 ++++- .../service/device/IotDeviceServiceImpl.java | 61 ++++++++++--------- .../control/IotDeviceUpstreamService.java | 12 ++-- .../control/IotDeviceUpstreamServiceImpl.java | 59 +++++++++++++++--- 11 files changed, 176 insertions(+), 48 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index a69ea180cc..b6068de636 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -53,6 +53,15 @@ public interface IotDeviceUpstreamApi { @PostMapping(PREFIX + "/register") CommonResult registerDevice(@Valid @RequestBody IotDeviceRegisterReqDTO registerReqDTO); + // TODO @芋艿:这个需要 plugins 接入下 + /** + * 注册子设备 + * + * @param registerReqDTO 注册子设备 DTO + */ + @PostMapping(PREFIX + "/register-sub") + CommonResult registerSubDevice(@Valid @RequestBody IotDeviceRegisterSubReqDTO registerReqDTO); + // ========== 插件相关 ========== /** diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java index a627cba421..cab55e832b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterReqDTO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; import lombok.Data; /** - * IoT 设备【注册】注册 Request DTO + * IoT 设备【注册】自己 Request DTO * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java new file mode 100644 index 0000000000..6e985f9559 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * IoT 设备【注册】子设备 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO { + + /** + * 子设备数组 + */ + @NotEmpty(message = "子设备不能为空") + private List params; + + /** + * 设备信息 + */ + @Data + public static class Device { + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index 6dd10a3865..7932844a70 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -29,8 +29,8 @@ public enum IotDeviceMessageIdentifierEnum { OTA_REPORT("report"), // 上行 REGISTER_REGISTER("register"), // 上行 - REGISTER_SUB_REGISTER("sub_register"), // 上行 - REGISTER_SUB_UNREGISTER("sub_unregister"),; // 下行 + REGISTER_REGISTER_SUB("register_sub"), // 上行 + REGISTER_UNREGISTER_SUB("unregister_sub"),; // 下行 /** * 标志符 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java index cc81e945bb..238e3e25ff 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java @@ -46,4 +46,14 @@ public enum IotProductDeviceTypeEnum implements ArrayValuable { return GATEWAY.getType().equals(type); } + /** + * 判断是否是网关子设备 + * + * @param type 类型 + * @return 是否是网关子设备 + */ + public static boolean isGatewaySub(Integer type) { + return GATEWAY_SUB.getType().equals(type); + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index 21b6c542a4..46a3e1f6cc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -49,6 +49,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { return success(true); } + @Override + public CommonResult registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) { + deviceUpstreamService.registerSubDevice(registerReqDTO); + return success(true); + } + // ========== 插件相关 ========== @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index 5da2f501b6..7bf0f33ee9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.Size; import lombok.Data; @@ -18,8 +17,7 @@ public class IotDeviceSaveReqVO { @Size(max = 50, message = "设备编号长度不能超过 50 个字符") private String deviceKey; - @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - @NotEmpty(message = "设备名称不能为空") + @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.AUTO, example = "王五") private String deviceName; @Schema(description = "备注名称", example = "张三") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 9ff0e81c29..fc6717bfdf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -30,10 +30,12 @@ public interface IotDeviceService { * * @param productKey 产品标识 * @param deviceName 设备名称 + * @param gatewayId 网关设备 ID * @return 设备 */ IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey, - @NotEmpty(message = "设备名称不能为空") String deviceName); + @NotEmpty(message = "设备名称不能为空") String deviceName, + Long gatewayId); /** * 更新设备 @@ -42,6 +44,17 @@ public interface IotDeviceService { */ void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); + // TODO @芋艿:先这么实现。未来看情况,要不要自己实现 + /** + * 更新设备的所属网关 + * + * @param id 编号 + * @param gatewayId 网关设备 ID + */ + default void updateDeviceGateway(Long id, Long gatewayId) { + updateDevice(new IotDeviceSaveReqVO().setId(id).setGatewayId(gatewayId)); + } + /** * 更新设备状态 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 6e20d724f9..144377fa00 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -64,22 +64,10 @@ public class IotDeviceServiceImpl implements IotDeviceService { if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } - // 1.2 校验设备标识是否唯一 - TenantUtils.executeIgnore(() -> { - if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) { - throw exception(PRODUCT_KEY_EXISTS); - } - }); - // 1.3 校验设备名称在同一产品下是否唯一 - if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceName()) != null) { - throw exception(DEVICE_NAME_EXISTS); - } - // 1.4 校验父设备是否为合法网关 - if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType()) - && createReqVO.getGatewayId() != null) { - validateGatewayDeviceExists(createReqVO.getGatewayId()); - } - // 1.5 校验分组存在 + // 1.2 统一校验 + validateCreateDeviceParam(product.getProductKey(), createReqVO.getDeviceName(), createReqVO.getDeviceKey(), + createReqVO.getGatewayId(), product); + // 1.3 校验分组存在 deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds()); // 2. 插入到数据库 @@ -90,34 +78,47 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @Override - public IotDeviceDO createDevice(String productKey, String deviceName) { + public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) { + String deviceKey = generateDeviceKey(); // 1.1 校验产品是否存在 IotProductDO product = TenantUtils.executeIgnore(() -> productService.getProductByProductKey(productKey)); if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } - // 1.2 校验设备标识是否唯一 - String deviceKey = generateDeviceKey(); - TenantUtils.executeIgnore(() -> { - if (deviceMapper.selectByDeviceKey(deviceKey) != null) { - throw exception(PRODUCT_KEY_EXISTS); - } - }); return TenantUtils.execute(product.getTenantId(), () -> { - // 1.3 校验设备名称在同一产品下是否唯一 - if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), deviceName) != null) { - throw exception(DEVICE_NAME_EXISTS); - } + // 1.2 校验设备名称在同一产品下是否唯一 + validateCreateDeviceParam(productKey, deviceName, deviceKey, gatewayId, product); // 2. 插入到数据库 - IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey); + IotDeviceDO device = new IotDeviceDO().setDeviceName(deviceName).setDeviceKey(deviceKey) + .setGatewayId(gatewayId); initDevice(device, product); deviceMapper.insert(device); return device; }); } + private void validateCreateDeviceParam(String productKey, String deviceName, String deviceKey, + Long gatewayId, IotProductDO product) { + TenantUtils.executeIgnore(() -> { + // 校验设备名称在同一产品下是否唯一 + if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) { + throw exception(DEVICE_NAME_EXISTS); + } + // 校验设备标识是否唯一 + if (deviceMapper.selectByDeviceKey(deviceKey) != null) { + throw exception(DEVICE_KEY_EXISTS); + } + }); + + // 校验父设备是否为合法网关 + if (IotProductDeviceTypeEnum.isGatewaySub(product.getDeviceType()) + && gatewayId != null) { + validateGatewayDeviceExists(gatewayId); + } + } + private void initDevice(IotDeviceDO device, IotProductDO product) { device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); // 生成并设置必要的字段 @@ -136,7 +137,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 1.1 校验存在 IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); // 1.2 校验父设备是否为合法网关 - if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType()) + if (IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType()) && updateReqVO.getGatewayId() != null) { validateGatewayDeviceExists(updateReqVO.getGatewayId()); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index 330444e345..41a8f8b5e7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -1,9 +1,6 @@ package cn.iocoder.yudao.module.iot.service.device.control; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceRegisterReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; import jakarta.validation.Valid; @@ -51,4 +48,11 @@ public interface IotDeviceUpstreamService { */ void registerDevice(IotDeviceRegisterReqDTO registerReqDTO); + /** + * 注册子设备 + * + * @param registerReqDTO 注册子设备 DTO + */ + void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index e3cd43bad0..14897ab10a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.device.control; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjUtil; @@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; @@ -164,20 +166,30 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { @Override public void registerDevice(IotDeviceRegisterReqDTO registerReqDTO) { - // 1.1 注册设备 log.info("[registerDevice][注册设备: {}]", registerReqDTO); - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); - boolean register = device == null; + registerDevice0(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), null, registerReqDTO); + } + + private void registerDevice0(String productKey, String deviceName, Long gatewayId, + IotDeviceUpstreamAbstractReqDTO registerReqDTO) { + // 1.1 注册设备 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName); + boolean registerNew = device == null; if (device == null) { - device = deviceService.createDevice(registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); - log.info("[registerDevice][请求({}) 成功注册设备({})]", registerReqDTO, device); + device = deviceService.createDevice(productKey, deviceName, gatewayId); + log.info("[registerDevice0][消息({}) 设备({}/{}) 成功注册]", registerReqDTO, productKey, device); + } else if (gatewayId != null && ObjUtil.notEqual(device.getGatewayId(), gatewayId)) { + Long deviceId = device.getId(); + TenantUtils.execute(device.getTenantId(), + () -> deviceService.updateDeviceGateway(deviceId, gatewayId)); + log.info("[registerDevice0][消息({}) 设备({}/{}) 更新网关设备编号({})]", + registerReqDTO, productKey, device, gatewayId); } // 1.2 记录设备的最后时间 updateDeviceLastTime(device, registerReqDTO); // 2. 发送设备消息 - if (register) { + if (registerNew) { IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class) .setType(IotDeviceMessageTypeEnum.REGISTER.getType()) .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER.getIdentifier()); @@ -185,6 +197,39 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { } } + @Override + public void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) { + // 1.1 注册子设备 + log.info("[registerSubDevice][注册子设备: {}]", registerReqDTO); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); + if (device == null) { + log.error("[registerSubDevice][设备({}/{}) 不存在]", + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName()); + return; + } + if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { + log.error("[registerSubDevice][设备({}/{}) 不是网关设备({}),无法进行注册]", + registerReqDTO.getProductKey(), registerReqDTO.getDeviceName(), device); + return; + } + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, registerReqDTO); + + // 2. 处理子设备 + if (CollUtil.isNotEmpty(registerReqDTO.getParams())) { + registerReqDTO.getParams().forEach(subDevice -> registerDevice0( + subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO)); + } + + // 3. 发送设备消息 + IotDeviceMessage message = BeanUtils.toBean(registerReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.REGISTER.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.REGISTER_REGISTER_SUB.getIdentifier()) + .setData(registerReqDTO.getParams()); + sendDeviceMessage(message, device); + } + private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { // 1. 【异步】记录设备与插件实例的映射 pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId()); From bc9b3715b1e6d03e6cb0aa117ec0e0afdd02137d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 8 Feb 2025 21:44:49 +0800 Subject: [PATCH 166/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT=EF=BC=9A=E8=AE=BE=E5=A4=87=E6=8B=93?= =?UTF-8?q?=E6=89=91=E5=9B=BE=E7=9A=84=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/api/device/IotDeviceUpstreamApi.java | 9 ++++ .../upstream/IotDeviceEventReportReqDTO.java | 2 + .../IotDevicePropertyReportReqDTO.java | 2 + .../upstream/IotDeviceRegisterSubReqDTO.java | 1 + .../upstream/IotDeviceStateUpdateReqDTO.java | 2 + .../upstream/IotDeviceTopologyAddReqDTO.java | 43 +++++++++++++++++ .../IotDeviceMessageIdentifierEnum.java | 5 +- .../device/IotDeviceMessageTypeEnum.java | 3 +- .../api/device/IoTDeviceUpstreamApiImpl.java | 6 +++ .../admin/device/IotDeviceController.java | 2 + .../iot/mq/message/IotDeviceMessage.java | 3 +- .../iot/service/device/IotDeviceService.java | 8 ++-- .../service/device/IotDeviceServiceImpl.java | 15 +++--- .../IotDeviceDownstreamServiceImpl.java | 1 + .../control/IotDeviceUpstreamService.java | 7 +++ .../control/IotDeviceUpstreamServiceImpl.java | 47 +++++++++++++++++++ .../upstream/IotDeviceUpstreamClient.java | 23 +++++++-- 17 files changed, 161 insertions(+), 18 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index b6068de636..7d198eba3b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -62,6 +62,15 @@ public interface IotDeviceUpstreamApi { @PostMapping(PREFIX + "/register-sub") CommonResult registerSubDevice(@Valid @RequestBody IotDeviceRegisterSubReqDTO registerReqDTO); + // TODO @芋艿:这个需要 plugins 接入下 + /** + * 注册设备拓扑 + * + * @param addReqDTO 注册设备拓扑 DTO + */ + @PostMapping(PREFIX + "/add-topology") + CommonResult addDeviceTopology(@Valid @RequestBody IotDeviceTopologyAddReqDTO addReqDTO); + // ========== 插件相关 ========== /** diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java index 8a7f3ff14d..34e6283d90 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java @@ -7,6 +7,8 @@ import java.util.Map; /** * IoT 设备【事件】上报 Request DTO + * + * @author 芋道源码 */ @Data public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java index c1654e69b3..4a276bd226 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java @@ -7,6 +7,8 @@ import java.util.Map; /** * IoT 设备【属性】上报 Request DTO + * + * @author 芋道源码 */ @Data public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java index 6e985f9559..0b826fbb14 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceRegisterSubReqDTO.java @@ -13,6 +13,7 @@ import java.util.List; @Data public class IotDeviceRegisterSubReqDTO extends IotDeviceUpstreamAbstractReqDTO { + // TODO @芋艿:看看要不要优化命名 /** * 子设备数组 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java index 02852cfe70..38c479a57b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java @@ -7,6 +7,8 @@ import lombok.Data; /** * IoT 设备【状态】更新 Request DTO + * + * @author 芋道源码 */ @Data public class IotDeviceStateUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java new file mode 100644 index 0000000000..38b2b69ef4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * IoT 设备【拓扑】添加 Request DTO + */ +@Data +public class IotDeviceTopologyAddReqDTO extends IotDeviceUpstreamAbstractReqDTO { + + // TODO @芋艿:看看要不要优化命名 + /** + * 子设备数组 + */ + @NotEmpty(message = "子设备不能为空") + private List params; + + /** + * 设备信息 + */ + @Data + public static class Device { + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + // TODO @芋艿:阿里云还有 sign 签名 + + } + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index 7932844a70..5bd169abae 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -30,7 +30,10 @@ public enum IotDeviceMessageIdentifierEnum { REGISTER_REGISTER("register"), // 上行 REGISTER_REGISTER_SUB("register_sub"), // 上行 - REGISTER_UNREGISTER_SUB("unregister_sub"),; // 下行 + REGISTER_UNREGISTER_SUB("unregister_sub"), // 下行 + + TOPOLOGY_ADD("topology_add"), // 下行; + ; /** * 标志符 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java index 000af57d46..0354157ed4 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageTypeEnum.java @@ -19,7 +19,8 @@ public enum IotDeviceMessageTypeEnum implements ArrayValuable { SERVICE("service"), // 设备服务:可参考 https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services 设备属性、事件、服务 CONFIG("config"), // 设备配置:可参考 https://help.aliyun.com/zh/iot/user-guide/remote-configuration-1 远程配置 OTA("ota"), // 设备 OTA:可参考 https://help.aliyun.com/zh/iot/user-guide/ota-update OTA 升级 - REGISTER("register"),; // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册 + REGISTER("register"), // 设备注册:可参考 https://help.aliyun.com/zh/iot/user-guide/register-devices 设备身份注册 + TOPOLOGY("topology"),; // 设备拓扑:可参考 https://help.aliyun.com/zh/iot/user-guide/manage-topological-relationships 设备拓扑 public static final String[] ARRAYS = Arrays.stream(values()).map(IotDeviceMessageTypeEnum::getType).toArray(String[]::new); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index 46a3e1f6cc..442c6b0ca0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -55,6 +55,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { return success(true); } + @Override + public CommonResult addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) { + deviceUpstreamService.addDeviceTopology(addReqDTO); + return success(true); + } + // ========== 插件相关 ========== @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 24d4263816..ac2c6ebd83 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -61,6 +61,8 @@ public class IotDeviceController { return success(true); } + // TODO @芋艿:参考阿里云:1)绑定网关;2)解绑网关 + @PutMapping("/update-group") @Operation(summary = "更新设备分组") @PreAuthorize("@ss.hasPermission('iot:device:update')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java index e4766755da..0e8309a821 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/message/IotDeviceMessage.java @@ -9,8 +9,9 @@ import lombok.NoArgsConstructor; import java.time.LocalDateTime; +// TODO @芋艿:参考阿里云的物模型,优化 IoT 上下行消息的设计,尽量保持一致(渐进式,不要一口气)! /** - * 设备消息 + * IoT 设备消息 */ @Data @NoArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index fc6717bfdf..24532d2548 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -30,12 +30,12 @@ public interface IotDeviceService { * * @param productKey 产品标识 * @param deviceName 设备名称 - * @param gatewayId 网关设备 ID + * @param gatewayId 网关设备 ID * @return 设备 */ IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey, - @NotEmpty(message = "设备名称不能为空") String deviceName, - Long gatewayId); + @NotEmpty(message = "设备名称不能为空") String deviceName, + Long gatewayId); /** * 更新设备 @@ -58,7 +58,7 @@ public interface IotDeviceService { /** * 更新设备状态 * - * @param id 编号 + * @param id 编号 * @param state 状态 */ void updateDeviceState(Long id, Integer state); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 144377fa00..66854e84b8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -81,8 +81,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { public IotDeviceDO createDevice(String productKey, String deviceName, Long gatewayId) { String deviceKey = generateDeviceKey(); // 1.1 校验产品是否存在 - IotProductDO product = TenantUtils.executeIgnore(() -> - productService.getProductByProductKey(productKey)); + IotProductDO product = TenantUtils.executeIgnore(() -> productService.getProductByProductKey(productKey)); if (product == null) { throw exception(PRODUCT_NOT_EXISTS); } @@ -100,7 +99,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { } private void validateCreateDeviceParam(String productKey, String deviceName, String deviceKey, - Long gatewayId, IotProductDO product) { + Long gatewayId, IotProductDO product) { TenantUtils.executeIgnore(() -> { // 校验设备名称在同一产品下是否唯一 if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) { @@ -120,7 +119,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { } private void initDevice(IotDeviceDO device, IotProductDO product) { - device.setProductId(product.getId()).setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType()); + device.setProductId(product.getId()).setProductKey(product.getProductKey()) + .setDeviceType(product.getDeviceType()); // 生成并设置必要的字段 // TODO @芋艿:各种 mqtt 是不是可以简化! device.setDeviceSecret(generateDeviceSecret()) @@ -269,7 +269,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2. 更新状态和时间 IotDeviceDO updateObj = new IotDeviceDO().setId(id).setState(state); if (device.getOnlineTime() == null - && Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) { + && Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) { updateObj.setActiveTime(LocalDateTime.now()); } if (Objects.equals(state, IotDeviceStateEnum.ONLINE.getState())) { @@ -363,7 +363,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { // 2.1.1 校验字段是否符合要求 try { ValidationUtils.validate(importDevice); - } catch (ConstraintViolationException ex){ + } catch (ConstraintViolationException ex) { respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage()); return; } @@ -427,7 +427,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { } @CacheEvict(value = RedisKeyConstants.DEVICE, key = "#device.productKey + '_' + #device.deviceName") - public void deleteDeviceCache0(IotDeviceDO device) {} + public void deleteDeviceCache0(IotDeviceDO device) { + } private IotDeviceServiceImpl getSelf() { return SpringUtil.getBean(getClass()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index 7c1ec5bb8b..3aab53de98 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -88,6 +88,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.OTA.getType())) { return otaUpgrade(downstreamReqVO, device, parentDevice); } + // TODO @芋艿:取消设备的网关的时,要不要下发 REGISTER_UNREGISTER_SUB ? throw new IllegalArgumentException("不支持的下行消息类型:" + downstreamReqVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index 41a8f8b5e7..39b0d19c05 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -55,4 +55,11 @@ public interface IotDeviceUpstreamService { */ void registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO); + /** + * 添加设备拓扑 + * + * @param addReqDTO 添加设备拓扑 DTO + */ + void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index 14897ab10a..ad065f930e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -220,6 +220,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { if (CollUtil.isNotEmpty(registerReqDTO.getParams())) { registerReqDTO.getParams().forEach(subDevice -> registerDevice0( subDevice.getProductKey(), subDevice.getDeviceName(), device.getId(), registerReqDTO)); + // TODO @芋艿:后续要处理,每个设备是否成功 } // 3. 发送设备消息 @@ -230,6 +231,52 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { sendDeviceMessage(message, device); } + @Override + public void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) { + // 1.1 获得设备 + log.info("[addDeviceTopology][添加设备拓扑: {}]", addReqDTO); + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + addReqDTO.getProductKey(), addReqDTO.getDeviceName()); + if (device == null) { + log.error("[addDeviceTopology][设备({}/{}) 不存在]", + addReqDTO.getProductKey(), addReqDTO.getDeviceName()); + return; + } + if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) { + log.error("[addDeviceTopology][设备({}/{}) 不是网关设备({}),无法进行拓扑添加]", + addReqDTO.getProductKey(), addReqDTO.getDeviceName(), device); + return; + } + // 1.2 记录设备的最后时间 + updateDeviceLastTime(device, addReqDTO); + + // 2. 处理拓扑 + if (CollUtil.isNotEmpty(addReqDTO.getParams())) { + TenantUtils.execute(device.getTenantId(), () -> { + addReqDTO.getParams().forEach(subDevice -> { + IotDeviceDO subDeviceDO = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + subDevice.getProductKey(), subDevice.getDeviceName()); + // TODO @芋艿:后续要处理,每个设备是否成功 + if (subDeviceDO == null) { + log.error("[addDeviceTopology][子设备({}/{}) 不存在]", + subDevice.getProductKey(), subDevice.getDeviceName()); + return; + } + deviceService.updateDeviceGateway(subDeviceDO.getId(), device.getId()); + log.info("[addDeviceTopology][子设备({}/{}) 添加到网关设备({}) 成功]", + subDevice.getProductKey(), subDevice.getDeviceName(), device); + }); + }); + } + + // 3. 发送设备消息 + IotDeviceMessage message = BeanUtils.toBean(addReqDTO, IotDeviceMessage.class) + .setType(IotDeviceMessageTypeEnum.TOPOLOGY.getType()) + .setIdentifier(IotDeviceMessageIdentifierEnum.TOPOLOGY_ADD.getIdentifier()) + .setData(addReqDTO.getParams()); + sendDeviceMessage(message, device); + } + private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { // 1. 【异步】记录设备与插件实例的映射 pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId()); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java index f3934cb0da..ec662510ba 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java @@ -2,10 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.upstream; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -42,6 +39,24 @@ public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { return doPost(url, reportReqDTO); } + // TODO @芋艿:待实现 + @Override + public CommonResult registerDevice(IotDeviceRegisterReqDTO registerReqDTO) { + return null; + } + + // TODO @芋艿:待实现 + @Override + public CommonResult registerSubDevice(IotDeviceRegisterSubReqDTO registerReqDTO) { + return null; + } + + // TODO @芋艿:待实现 + @Override + public CommonResult addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO) { + return null; + } + @Override public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property"; From 949fa225093bd4e9d0c9115b95dbb693411ef852 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Feb 2025 07:15:59 +0800 Subject: [PATCH 167/309] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=89=8D=E7=BC=80=EF=BC=8C=E6=97=A5=E5=BF=97=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=8F=92=E5=85=A5=20#731?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/framework/web/core/filter/ApiRequestFilter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java index b702515590..7b064e8d6e 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/ApiRequestFilter.java @@ -20,8 +20,8 @@ public abstract class ApiRequestFilter extends OncePerRequestFilter { @Override protected boolean shouldNotFilter(HttpServletRequest request) { // 只过滤 API 请求的地址 - return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(), - webProperties.getAppApi().getPrefix()); + String apiUri = request.getRequestURI().substring(request.getContextPath().length()); + return !StrUtil.startWithAny(apiUri, webProperties.getAdminApi().getPrefix(), webProperties.getAppApi().getPrefix()); } } From d116e5eec1361c74bef04dd001884adb43fb1e26 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Feb 2025 07:26:01 +0800 Subject: [PATCH 168/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E7=89=A9=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E7=9A=84=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/controller/admin/thingmodel/model/ThingModelEvent.java | 2 +- .../iot/controller/admin/thingmodel/model/ThingModelParam.java | 2 +- .../controller/admin/thingmodel/model/ThingModelProperty.java | 2 +- .../controller/admin/thingmodel/model/ThingModelService.java | 2 +- .../iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index 151d1f571e..d3f0d33c5c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -21,7 +21,7 @@ public class ThingModelEvent { * 事件标识符 */ @NotEmpty(message = "事件标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "事件标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "事件标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") private String identifier; /** * 事件名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java index dc1e7deac6..89b162db91 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -22,7 +22,7 @@ public class ThingModelParam { * 参数标识符 */ @NotEmpty(message = "参数标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "参数标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "参数标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") private String identifier; /** * 参数名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 866f25b84e..395c8611e4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -24,7 +24,7 @@ public class ThingModelProperty { * 属性标识符 */ @NotEmpty(message = "属性标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "属性标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") private String identifier; /** * 属性名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 7db2b5f7a3..7f97b94029 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -21,7 +21,7 @@ public class ThingModelService { * 服务标识符 */ @NotEmpty(message = "服务标识符不能为空") - @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "服务标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过32个字符") + @Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{0,31}$", message = "服务标识符只能由字母、数字和下划线组成,必须以字母开头,长度不超过 32 个字符") private String identifier; /** * 服务名称 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java index 72565a8c45..15a5b9f959 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java @@ -15,7 +15,7 @@ import java.time.LocalDateTime; @ExcelIgnoreUnannotated public class IotThingModelRespVO { - @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21816") @ExcelProperty("产品ID") private Long id; From d75d71d7d0e1308399d6e8d94acf30e2d834d622 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Feb 2025 07:26:30 +0800 Subject: [PATCH 169/309] =?UTF-8?q?1218=20=E5=A6=82=E6=9E=9C=E6=B2=A1?= =?UTF-8?q?=E6=9F=A5=E5=88=B0=E7=94=A8=E6=88=B7=E4=B9=9F=E6=9C=89=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E6=98=AF=E7=A9=BA=E7=BB=93=E6=9E=9C=E9=9B=86=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E4=B8=80=E5=AE=9A=E6=98=AFnull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade/service/order/TradeOrderQueryServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java index 4522f956af..54b2aa4185 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java @@ -91,7 +91,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { public PageResult getOrderPage(TradeOrderPageReqVO reqVO) { // 根据用户查询条件构建用户编号列表 Set userIds = buildQueryConditionUserIds(reqVO); - if (userIds == null) { // 没查询到用户,说明肯定也没他的订单 + if (CollUtil.isEmpty(userIds)) { // 没查询到用户,说明肯定也没他的订单 return PageResult.empty(); } // 分页查询 @@ -122,7 +122,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) { // 根据用户查询条件构建用户编号列表 Set userIds = buildQueryConditionUserIds(reqVO); - if (userIds == null) { // 没查询到用户,说明肯定也没他的订单 + if (CollUtil.isEmpty(userIds)) { // 没查询到用户,说明肯定也没他的订单 return new TradeOrderSummaryRespVO(); } // 查询每个售后状态对应的数量、金额 From cf7ff1ca8ab8d15e76885cc7424f5588caaa3160 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Feb 2025 08:11:52 +0800 Subject: [PATCH 170/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91CRM=EF=BC=9ACrmBusinessController=20=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=9D=83=E9=99=90=E6=A0=87=E8=AF=86=E4=B8=8D=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crm/controller/admin/business/CrmBusinessController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java index 7c2dae2233..33e8d84abd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java @@ -127,8 +127,8 @@ public class CrmBusinessController { } @GetMapping("/simple-all-list") - @Operation(summary = "获得联系人的精简列表") - @PreAuthorize("@ss.hasPermission('crm:contact:query')") + @Operation(summary = "获得商机的精简列表") + @PreAuthorize("@ss.hasPermission('crm:business:query')") public CommonResult> getSimpleContactList() { CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO(); reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页 From 2d18f593ae8d4770a2f3c957c8c84c339bd7210e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Feb 2025 08:11:52 +0800 Subject: [PATCH 171/309] =?UTF-8?q?V2.4.1=20=E5=8F=91=E5=B8=83~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- yudao-dependencies/pom.xml | 2 +- .../crm/controller/admin/business/CrmBusinessController.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 43da0c8d03..142869ff97 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 2.4.0-SNAPSHOT + 2.4.1-SNAPSHOT 17 ${java.version} diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 16b713a1b2..d6c0a642fc 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -14,7 +14,7 @@ https://github.com/YunaiV/ruoyi-vue-pro - 2.4.0-SNAPSHOT + 2.4.1-SNAPSHOT 1.6.0 3.4.1 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java index 7c2dae2233..33e8d84abd 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java @@ -127,8 +127,8 @@ public class CrmBusinessController { } @GetMapping("/simple-all-list") - @Operation(summary = "获得联系人的精简列表") - @PreAuthorize("@ss.hasPermission('crm:contact:query')") + @Operation(summary = "获得商机的精简列表") + @PreAuthorize("@ss.hasPermission('crm:business:query')") public CommonResult> getSimpleContactList() { CrmBusinessPageReqVO reqVO = new CrmBusinessPageReqVO(); reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页 From 2ab2e45465b268e288790dad9a742ee47f3f54ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E6=98=AF=E7=9D=A1=E7=9D=80=E7=9A=84=E6=B0=B4?= <850083043@qq.com> Date: Tue, 11 Feb 2025 06:15:30 +0000 Subject: [PATCH 172/309] =?UTF-8?q?=E6=9B=BF=E6=8D=A2javax=E4=B8=BAjakarta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 冰是睡着的水 <850083043@qq.com> --- .../yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java | 2 +- .../yudao/module/iot/job/plugin/IotPluginInstancesJob.java | 2 +- .../mq/consumer/device/IotDevicePropertyMessageConsumer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index 442c6b0ca0..699f3dd754 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java index 1d24417377..ff93dc8db0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/IotPluginInstancesJob.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; import org.springframework.stereotype.Component; -import javax.annotation.Resource; +import jakarta.annotation.Resource; import java.time.Duration; import java.time.LocalDateTime; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java index 3f7fe10890..bf9cc5332c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDevicePropertyMessageConsumer.java @@ -10,7 +10,7 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import javax.annotation.Resource; +import jakarta.annotation.Resource; /** * 针对 {@link IotDeviceMessage} 的消费者,记录设备属性 From c27b02beb634aa8c702353efface531d2562d76c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 12 Feb 2025 18:19:53 +0800 Subject: [PATCH 173/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=BE=85=E5=AE=9A=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/dal/dataobject/device/IotDeviceDO.java | 2 ++ .../module/iot/dal/tdengine/IotDevicePropertyMapper.java | 3 ++- .../module/iot/service/product/IotProductServiceImpl.java | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index d991454765..4c52031fe6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -122,6 +122,7 @@ public class IotDeviceDO extends TenantBaseDO { */ private String firmwareId; + // TODO @芋艿:【待定 003】:要不要增加 username?目前 tl 有,阿里云之类的没有 /** * 设备密钥,用于设备认证,需安全存储 */ @@ -144,6 +145,7 @@ public class IotDeviceDO extends TenantBaseDO { // TODO @haohao:是不是要枚举哈 private String authType; + // TODO @芋艿:【待定 002】:1)设备维护的时候,设置位置?类似 tl?;2)设备上传的时候,设置位置,类似 it? /** * 设备位置的纬度 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index f2d6af97e9..e506b49d4b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -32,6 +32,7 @@ public interface IotDevicePropertyMapper { default void alterProductPropertySTable(String productKey, List oldFields, List newFields) { + // TODO @芋艿:需要处理 device_key,重新发布的时候 oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), TDengineTableField.FIELD_TS, "report_time")); List addFields = newFields.stream().filter( // 新增的字段 @@ -51,7 +52,7 @@ public interface IotDevicePropertyMapper { modifyTypeFields.add(newField); return; } - if (newField.getLength()!= null) { + if (newField.getLength() != null) { if (newField.getLength() > oldField.getLength()) { modifyLengthFields.add(newField); } else if (newField.getLength() < oldField.getLength()) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index fc2300b7cb..b77a7e0e1b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -121,9 +121,9 @@ public class IotProductServiceImpl implements IotProductService { // 1. 校验存在 validateProductExists(id); - // 2. 产品是发布状态 + // 2. 更新为发布状态,需要创建产品超级表数据模型 + // TODO @芋艿:【待定 001】1)是否需要操作后,在 redis 进行缓存,实现一个“快照”的情况,类似 tl; if (Objects.equals(status, IotProductStatusEnum.PUBLISHED.getStatus())) { - // 创建产品超级表数据模型 devicePropertyDataService.defineDevicePropertyData(id); } From 3ab7ad484a2be9ad9d632ea8147d2ba30814d077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Fri, 14 Feb 2025 09:34:25 +0800 Subject: [PATCH 174/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E5=A2=9E=E5=BC=BA=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E5=92=8C=E5=81=9C=E6=AD=A2=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=E9=94=99=E8=AF=AF=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 2 ++ yudao-module-iot/yudao-module-iot-biz/pom.xml | 16 ++++----- .../plugin/IotPluginInstanceServiceImpl.java | 14 ++++++-- .../yudao-module-iot-plugin-http/pom.xml | 34 +++++++++++++++---- .../http/config/IotHttpVertxPlugin.java | 21 ++++-------- 5 files changed, 56 insertions(+), 31 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index e85d4b368f..cd065aaf52 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -46,6 +46,8 @@ public interface ErrorCodeConstants { ErrorCode PLUGIN_CONFIG_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件"); ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效"); ErrorCode PLUGIN_CONFIG_KEY_DUPLICATE = new ErrorCode(1_050_006_005, "插件标识已存在"); + ErrorCode PLUGIN_START_FAILED = new ErrorCode(1_050_006_006, "插件启动失败"); + ErrorCode PLUGIN_STOP_FAILED = new ErrorCode(1_050_006_007, "插件停止失败"); // ========== 插件实例 1-050-007-000 ========== diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 6f01e33191..780817848b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -65,16 +65,16 @@ - - io.vertx - vertx-web - + + + + - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 - + + + + org.pf4j diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java index 8c9973a703..3c15ff774b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInstanceServiceImpl.java @@ -191,13 +191,23 @@ public class IotPluginInstanceServiceImpl implements IotPluginInstanceService { // 启动插件 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) { - pluginManager.startPlugin(pluginKey); + try { + pluginManager.startPlugin(pluginKey); + } catch (Exception e) { + log.error("[updatePluginStatus][启动插件({}) 失败]", pluginKey, e); + throw exception(ErrorCodeConstants.PLUGIN_START_FAILED, e); + } log.info("已启动插件: {}", pluginKey); } // 停止插件 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) { - pluginManager.stopPlugin(pluginKey); + try { + pluginManager.stopPlugin(pluginKey); + } catch (Exception e) { + log.error("[updatePluginStatus][停止插件({}) 失败]", pluginKey, e); + throw exception(ErrorCodeConstants.PLUGIN_STOP_FAILED, e); + } log.info("已停止插件: {}", pluginKey); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml index 0f7e092f0d..88a413ca67 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml @@ -94,6 +94,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -112,12 +140,6 @@ - - maven-deploy-plugin - - true - - diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java index fe789af52d..ac9a933401 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java @@ -59,22 +59,13 @@ public class IotHttpVertxPlugin extends SpringPlugin { @Override protected ApplicationContext createApplicationContext() { - // TODO @haohao:这个加 deviceDataApi 的目的是啥呀? - AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext() { - - @Override - protected void prepareRefresh() { - // 在刷新容器前注册主程序中的 Bean - ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); - IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); - beanFactory.registerSingleton("deviceDataApi", deviceDataApi); - super.prepareRefresh(); - } - - }; - + // 创建插件自己的 ApplicationContext + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext(); + // 设置父容器为主应用的 ApplicationContext (确保主应用中提供的类可用) + pluginContext.setParent(SpringUtil.getApplicationContext()); + // 继续使用插件自己的 ClassLoader 以加载插件内部的类 pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); - // TODO @芋艿:枚举 + // 扫描当前插件的自动配置包 pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); pluginContext.refresh(); return pluginContext; From 75bca650da7954bac584d46a4cac428dbfbf49ca Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 08:29:50 +0800 Subject: [PATCH 175/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=BE=85=E5=AE=9A=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/service/device/data/IotDevicePropertyServiceImpl.java | 1 + .../upstream/router/IotDevicePropertyReportVertxHandler.java | 1 + 2 files changed, 2 insertions(+) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java index 4bd25a04d0..77dde64a66 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDevicePropertyServiceImpl.java @@ -133,6 +133,7 @@ public class IotDevicePropertyServiceImpl implements IotDevicePropertyService { } // 2. 根据物模型,拼接合法的属性 + // TODO @芋艿:【待定 004】赋能后,属性到底以 thingModel 为准(ik),还是 db 的表结构为准(tl)? List thingModels = thingModelService.getThingModelListByProductKeyFromCache(device.getProductKey()); Map properties = new HashMap<>(); ((Map) message.getData()).forEach((key, value) -> { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java index c22a6fe996..22fb1be821 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java @@ -20,6 +20,7 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; +// TODO @芋艿:【待定 005】要不要简化成,解析后,统一处理?只有一个 Handler!!! /** * IoT 设备属性上报的 Vert.x Handler * From 3c10dcf392c9d1a2175695edeff906580994bb0d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 10:02:48 +0800 Subject: [PATCH 176/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91MALL=EF=BC=9A=E7=AE=A1=E7=90=86=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=EF=BC=8C=E6=97=A0=E6=B3=95=E6=9F=A5=E8=AF=A2=E5=88=B0?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade/service/order/TradeOrderQueryServiceImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java index 54b2aa4185..41224f45ae 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java @@ -91,9 +91,10 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { public PageResult getOrderPage(TradeOrderPageReqVO reqVO) { // 根据用户查询条件构建用户编号列表 Set userIds = buildQueryConditionUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { // 没查询到用户,说明肯定也没他的订单 + if (userIds == null) { // 没查询到用户,说明肯定也没他的订单 return PageResult.empty(); } + // 分页查询 return tradeOrderMapper.selectPage(reqVO, userIds); } @@ -122,11 +123,11 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { public TradeOrderSummaryRespVO getOrderSummary(TradeOrderPageReqVO reqVO) { // 根据用户查询条件构建用户编号列表 Set userIds = buildQueryConditionUserIds(reqVO); - if (CollUtil.isEmpty(userIds)) { // 没查询到用户,说明肯定也没他的订单 + if (userIds == null) { // 没查询到用户,说明肯定也没他的订单 return new TradeOrderSummaryRespVO(); } // 查询每个售后状态对应的数量、金额 - List> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, null); + List> list = tradeOrderMapper.selectOrderSummaryGroupByRefundStatus(reqVO, userIds); TradeOrderSummaryRespVO vo = new TradeOrderSummaryRespVO().setAfterSaleCount(0L).setAfterSalePrice(0L); for (Map map : list) { From add90365df8d863bbbfe5fa0323032f5b0e4aa4d Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 19 Feb 2025 15:51:34 +0800 Subject: [PATCH 177/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20RocketMQConfig=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E7=8E=B0executeRocketMQ=20=E5=8F=91=E9=80=81?= =?UTF-8?q?=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 2 +- .../yudao-spring-boot-starter-mq/pom.xml | 1 - yudao-module-iot/yudao-module-iot-biz/pom.xml | 5 ++ .../dal/dataobject/rule/IotDataBridgeDO.java | 37 ++++++++++ .../action/IotRuleSceneDataBridgeAction.java | 73 +++++++++++++++++++ 5 files changed, 116 insertions(+), 2 deletions(-) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 05d9aa2b96..efdeddde13 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -34,7 +34,7 @@ 5.1.0 3.3.3 - 2.3.1 + 2.3.2 2.2.7 diff --git a/yudao-framework/yudao-spring-boot-starter-mq/pom.xml b/yudao-framework/yudao-spring-boot-starter-mq/pom.xml index c8972f16b2..15bbaa42de 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mq/pom.xml @@ -36,7 +36,6 @@ org.apache.rocketmq rocketmq-spring-boot-starter - true diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 780817848b..2a9669c0ee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -75,6 +75,11 @@ + + + cn.iocoder.boot + yudao-spring-boot-starter-mq + org.pf4j diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index 568377dd02..b3a863c8be 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -136,5 +136,42 @@ public class IotDataBridgeDO extends BaseDO { } + /** + * RocketMQ 配置 + */ + @Data + public static class RocketMQConfig implements Config { + + /** + * RocketMQ 名称服务器地址 + */ + private String nameServer; + + /** + * 生产者/消费者组 + */ + private String group; + + /** + * 主题 + */ + private String topic; + + /** + * 标签 + */ + private String tags; + + /** + * 访问密钥 + */ + private String accessKey; + + /** + * 秘密钥匙 + */ + private String secretKey; + + } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index 82b0467140..a70b6e8620 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -13,11 +13,17 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.remoting.common.RemotingHelper; import org.springframework.http.*; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @@ -64,6 +70,11 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig()); return; } + // 2.2 执行 RocketMQ 发送消息 + if (IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) { + executeRocketMQ(message, (IotDataBridgeDO.RocketMQConfig) dataBridge.getConfig()); + return; + } // TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; // TODO @芋艿:mq-redis @@ -131,4 +142,66 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { } } + private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { + // 1. 创建生产者实例,指定生产者组名 + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); + try { + // 2. 设置 NameServer 地址 + producer.setNamesrvAddr(config.getNameServer()); + // 3. 启动生产者 + producer.start(); + // 4. 创建消息对象,指定Topic、Tag和消息体 + Message msg = new Message( + config.getTopic(), + config.getTags(), + message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) + ); + + // 5. 发送同步消息并处理结果 + SendResult sendResult = producer.send(msg); + // 6. 处理发送结果 + if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { + log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", + message, config, sendResult); + } else { + log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", + message, config, sendResult); + } + } catch (Exception e) { + log.error("[executeRocketMQ][message({}) config({}) 发送异常]", + message, config, e); + } finally { + // 7. 关闭生产者 + producer.shutdown(); + } + } + + public static void main(String[] args) { + // 1. 创建 IotRuleSceneDataBridgeAction 实例 + IotRuleSceneDataBridgeAction action = new IotRuleSceneDataBridgeAction(); + + // 2. 创建测试消息 + IotDeviceMessage message = IotDeviceMessage.builder() + .requestId("TEST-001") + .productKey("testProduct") + .deviceName("testDevice") + .deviceKey("testDeviceKey") + .type("property") + .identifier("temperature") + .data("{\"value\": 60}") + .reportTime(LocalDateTime.now()) + .tenantId(1L) + .build(); + + // 3. 创建 RocketMQ 配置 + IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig(); + config.setNameServer("127.0.0.1:9876"); + config.setGroup("test-group"); + config.setTopic("test-topic"); + config.setTags("test-tag"); + + // 4. 执行测试 + action.executeRocketMQ(message, config); + } + } From 8e7bbfe0daed7d598c62396d7db0a771bc5768b4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 22:42:19 +0800 Subject: [PATCH 178/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9Arocketmq=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A1=A5=E6=8E=A5=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dal/dataobject/rule/IotDataBridgeDO.java | 30 ++++++++----------- .../action/IotRuleSceneDataBridgeAction.java | 27 ++++++++--------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index b3a863c8be..213b0cda10 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -146,32 +146,28 @@ public class IotDataBridgeDO extends BaseDO { * RocketMQ 名称服务器地址 */ private String nameServer; - - /** - * 生产者/消费者组 - */ - private String group; - - /** - * 主题 - */ - private String topic; - - /** - * 标签 - */ - private String tags; - /** * 访问密钥 */ private String accessKey; - /** * 秘密钥匙 */ private String secretKey; + /** + * 生产者组 + */ + private String group; + /** + * 主题 + */ + private String topic; + /** + * 标签 + */ + private String tags; + } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index a70b6e8620..7668c05b94 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -143,39 +143,38 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { } private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { - // 1. 创建生产者实例,指定生产者组名 + // 1.1 创建生产者实例,指定生产者组名 DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); + // TODO @puhui999:可以考虑,基于 guava 做 cache,使用 config 作为 key,然后假个 listener 超时,销毁 producer try { - // 2. 设置 NameServer 地址 + // 1.2 设置 NameServer 地址 producer.setNamesrvAddr(config.getNameServer()); - // 3. 启动生产者 + // 1.3 启动生产者 producer.start(); - // 4. 创建消息对象,指定Topic、Tag和消息体 + + // 2.1 创建消息对象,指定Topic、Tag和消息体 Message msg = new Message( config.getTopic(), config.getTags(), message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) ); - - // 5. 发送同步消息并处理结果 + // 2.2 发送同步消息并处理结果 SendResult sendResult = producer.send(msg); - // 6. 处理发送结果 + // 2.3 处理发送结果 if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { - log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", - message, config, sendResult); + log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); } else { - log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", - message, config, sendResult); + log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); } } catch (Exception e) { - log.error("[executeRocketMQ][message({}) config({}) 发送异常]", - message, config, e); + log.error("[executeRocketMQ][message({}) config({}) 发送异常]", message, config, e); } finally { - // 7. 关闭生产者 + // 3. 关闭生产者 producer.shutdown(); } } + // TODO @芋艿:测试代码,后续清理 public static void main(String[] args) { // 1. 创建 IotRuleSceneDataBridgeAction 实例 IotRuleSceneDataBridgeAction action = new IotRuleSceneDataBridgeAction(); From 54381e29a7dd634607d27bbf9ee9d601c9bc0b4d Mon Sep 17 00:00:00 2001 From: Shelly Chan <15732273052@139.com> Date: Thu, 20 Feb 2025 01:05:41 +0800 Subject: [PATCH 179/309] =?UTF-8?q?feat(iot):=20=E6=B7=BB=E5=8A=A0=20OTA?= =?UTF-8?q?=20=E5=9B=BA=E4=BB=B6=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 OTA 固件相关错误码 - 实现 OTA 固件创建、更新和查询接口 - 添加 OTA 升级记录相关功能 - 实现 OTA 固件升级任务定时处理 --- .../module/iot/enums/ErrorCodeConstants.java | 12 + .../admin/ota/IotOtaFirmwareController.java | 62 +++++ .../ota/IotOtaUpgradeRecordController.java | 76 ++++++ .../ota/IotOtaUpgradeTaskController.java | 64 +++++ .../firmware/IotOtaFirmwareCommonReqVO.java | 26 ++ .../firmware/IotOtaFirmwareCreateReqVO.java | 73 ++++++ .../vo/firmware/IotOtaFirmwarePageReqVO.java | 23 ++ .../ota/vo/firmware/IotOtaFirmwareRespVO.java | 85 +++++++ .../firmware/IotOtaFirmwareUpdateReqVO.java | 20 ++ .../record/IotOtaUpgradeRecordPageReqVO.java | 56 +++++ .../record/IotOtaUpgradeRecordRespVO.java | 109 ++++++++ .../task/IotOtaUpgradeTaskPageReqVO.java | 27 ++ .../upgrade/task/IotOtaUpgradeTaskRespVO.java | 84 +++++++ .../task/IotOtaUpgradeTaskSaveReqVO.java | 68 +++++ .../ota/IotOtaUpgradeRecordConvert.java | 34 +++ .../dataobject/ota/IotOtaUpgradeTaskDO.java | 21 +- .../iot/dal/mysql/device/IotDeviceMapper.java | 4 + .../dal/mysql/ota/IotOtaFirmwareMapper.java | 46 ++++ .../mysql/ota/IotOtaUpgradeRecordMapper.java | 133 ++++++++++ .../mysql/ota/IotOtaUpgradeTaskMapper.java | 59 +++++ .../iot/job/ota/IotOtaUpgradeRecordJob.java | 34 +++ .../iot/job/ota/IotOtaUpgradeTaskJob.java | 71 ++++++ .../iot/service/device/IotDeviceService.java | 23 +- .../service/device/IotDeviceServiceImpl.java | 12 +- .../service/ota/IotOtaFirmwareService.java | 57 +++++ .../ota/IotOtaFirmwareServiceImpl.java | 88 +++++++ .../ota/IotOtaUpgradeRecordService.java | 112 +++++++++ .../ota/IotOtaUpgradeRecordServiceImpl.java | 207 +++++++++++++++ .../service/ota/IotOtaUpgradeTaskService.java | 67 +++++ .../ota/IotOtaUpgradeTaskServiceImpl.java | 236 ++++++++++++++++++ .../iot/service/ota/bo/package-info.java | 1 + .../service/ota/bo/upgrade/package-info.java | 1 + .../IotOtaUpgradeRecordCreateReqBO.java | 79 ++++++ .../IotOtaUpgradeRecordUpdateReqBO.java | 45 ++++ .../mapper/ota/IotOtaUpgradeRecordMapper.xml | 22 ++ 35 files changed, 2128 insertions(+), 9 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index e85d4b368f..3077300650 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -49,5 +49,17 @@ public interface ErrorCodeConstants { // ========== 插件实例 1-050-007-000 ========== + // ========== 固件相关 1-050-008-000 ========== + ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在"); + ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复"); + + ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_002, "升级任务不存在"); + ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_003, "升级任务名称重复"); + ErrorCode OTA_UPGRADE_TASK_PARAMS_INVALID = new ErrorCode(1_050_008_004, "升级任务参数无效"); + ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_005, "升级任务不能取消"); + + ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_006, "升级记录不存在"); + ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_007, "升级记录重复"); + ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_008, "升级记录不能重试"); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java new file mode 100644 index 0000000000..2771e35b58 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.service.ota.IotOtaFirmwareService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Validated +@RestController +@Tag(name = "管理后台 - IoT OTA固件") +@RequestMapping("/iot/ota-firmware") +public class IotOtaFirmwareController { + + @Resource + private IotOtaFirmwareService otaFirmwareService; + + @PostMapping("/create") + @Operation(summary = "创建OTA固件") + @PreAuthorize("@ss.hasPermission('iot:ota-firmware:create')") + public CommonResult createOtaFirmware(@Valid @RequestBody IotOtaFirmwareCreateReqVO createReqVO) { + return success(otaFirmwareService.createOtaFirmware(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新OTA固件") + @PreAuthorize("@ss.hasPermission('iot:ota-firmware:update')") + public CommonResult updateOtaFirmware(@Valid @RequestBody IotOtaFirmwareUpdateReqVO updateReqVO) { + otaFirmwareService.updateOtaFirmware(updateReqVO); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得OTA固件") + @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") + public CommonResult getOtaFirmware(@RequestParam("id") Long id) { + IotOtaFirmwareDO otaFirmware = otaFirmwareService.getOtaFirmware(id); + return success(BeanUtils.toBean(otaFirmware, IotOtaFirmwareRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得OTA固件分页") + @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") + public CommonResult> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO) { + PageResult pageResult = otaFirmwareService.getOtaFirmwarePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotOtaFirmwareRespVO.class)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java new file mode 100644 index 0000000000..e28f721c81 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Validated +@RestController +@Tag(name = "管理后台 - OTA升级记录") +@RequestMapping("/iot/ota-upgrade-record") +public class IotOtaUpgradeRecordController { + + @Resource + private IotOtaUpgradeRecordService upgradeRecordService; + + @GetMapping("/get-count") + @Operation(summary = "获得升级记录 分页 tab count") + @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") + public CommonResult> getOtaUpgradeRecordCount( + @Valid IotOtaUpgradeRecordPageReqVO pageReqVO) { + return success(upgradeRecordService.getOtaUpgradeRecordCount(pageReqVO)); + } + + @GetMapping("/get-statistics") + @Operation(summary = "固件升级设备统计") + @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") + @Parameter(name = "firmwareId", description = "固件编号", required = true, example = "1024") + public CommonResult> getOtaUpgradeRecordStatistics( + @RequestParam(value = "firmwareId") Long firmwareId) { + return success(upgradeRecordService.getOtaUpgradeRecordStatistics(firmwareId)); + } + + @GetMapping("/page") + @Operation(summary = "获得升级记录分页") + @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") + public CommonResult> getUpgradeRecordPage( + @Valid IotOtaUpgradeRecordPageReqVO pageReqVO) { + PageResult pageResult = upgradeRecordService.getUpgradeRecordPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotOtaUpgradeRecordRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得升级记录") + @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") + @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024") + public CommonResult getUpgradeRecord(@RequestParam("id") Long id) { + IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordService.getUpgradeRecord(id); + return success(BeanUtils.toBean(upgradeRecord, IotOtaUpgradeRecordRespVO.class)); + } + + @PostMapping("/retry") + @Operation(summary = "重试升级记录") + @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:retry')") + @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024") + public CommonResult retryUpgradeRecord(@RequestParam("id") Long id) { + upgradeRecordService.retryUpgradeRecord(id); + return success(true); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java new file mode 100644 index 0000000000..20216fecc7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeTaskService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Validated +@RestController +@Tag(name = "管理后台 - OTA升级任务") +@RequestMapping("/iot/ota-upgrade-task") +public class IotOtaUpgradeTaskController { + + @Resource + private IotOtaUpgradeTaskService upgradeTaskService; + + @PostMapping("/create") + @Operation(summary = "创建升级任务") + @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:create')") + public CommonResult createUpgradeTask(@Valid @RequestBody IotOtaUpgradeTaskSaveReqVO createReqVO) { + return success(upgradeTaskService.createUpgradeTask(createReqVO)); + } + + @PostMapping("/cancel") + @Operation(summary = "取消升级任务") + @Parameter(name = "id", description = "升级任务编号", required = true) + @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:cancel')") + public CommonResult cancelUpgradeTask(@RequestParam("id") Long id) { + upgradeTaskService.cancelUpgradeTask(id); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得升级任务分页") + @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:query')") + public CommonResult> getUpgradeTaskPage(@Valid @RequestBody IotOtaUpgradeTaskPageReqVO pageReqVO) { + PageResult pageResult = upgradeTaskService.getUpgradeTaskPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotOtaUpgradeTaskRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得升级任务") + @Parameter(name = "id", description = "升级任务编号", required = true, example = "1024") + @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:query')") + public CommonResult getUpgradeTask(@RequestParam("id") Long id) { + IotOtaUpgradeTaskDO upgradeTask = upgradeTaskService.getUpgradeTask(id); + return success(BeanUtils.toBean(upgradeTask, IotOtaUpgradeTaskRespVO.class)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java new file mode 100644 index 0000000000..01f10caa56 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA固件信息 Request VO") +public class IotOtaFirmwareCommonReqVO { + + /** + * 固件名称 + */ + @NotEmpty(message = "固件名称不能为空") + @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件") + private String name; + + /** + * 固件描述 + */ + @Schema(description = "固件描述", example = "某品牌型号固件,测试用") + private String description; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java new file mode 100644 index 0000000000..44d942c633 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA固件创建 Request VO") +public class IotOtaFirmwareCreateReqVO extends IotOtaFirmwareCommonReqVO { + + /** + * 版本号 + */ + @NotEmpty(message = "版本号不能为空") + @Schema(description = "版本号", requiredMode = REQUIRED, example = "1.0.0") + private String version; + + /** + * 产品编号 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} + */ + @NotNull(message = "产品编号不能为空") + @Schema(description = "产品编号", requiredMode = REQUIRED, example = "1024") + private String productId; + + /** + * 产品标识 + *

+ * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()} + */ + @NotEmpty(message = "产品标识不能为空") + @Schema(description = "产品标识", requiredMode = REQUIRED, example = "yudao") + private String productKey; + + /** + * 签名方式 + *

+ * 例如说:MD5、SHA256 + */ + @Schema(description = "签名方式", example = "MD5") + private String signMethod; + + /** + * 固件文件签名 + */ + @Schema(description = "固件文件签名", example = "d41d8cd98f00b204e9800998ecf8427e") + private String fileSign; + + /** + * 固件文件大小 + */ + @NotNull(message = "固件文件大小不能为空") + @Schema(description = "固件文件大小(单位:byte)", example = "1024") + private Long fileSize; + + /** + * 固件文件 URL + */ + @NotEmpty(message = "固件文件 URL 不能为空") + @Schema(description = "固件文件 URL", requiredMode = REQUIRED, example = "https://www.iocoder.cn/yudao-firmware.zip") + private String fileUrl; + + /** + * 自定义信息,建议使用 JSON 格式 + */ + @Schema(description = "自定义信息,建议使用 JSON 格式", example = "{\"key1\":\"value1\",\"key2\":\"value2\"}") + private String information; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java new file mode 100644 index 0000000000..8ae3e51994 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(description = "管理后台 - OTA固件分页 Request VO") +public class IotOtaFirmwarePageReqVO extends PageParam { + + /** + * 固件名称 + */ + @Schema(description = "固件名称", example = "智能开关固件") + private String name; + + /** + * 产品标识 + */ + @Schema(description = "产品标识", example = "1024") + private String productId; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java new file mode 100644 index 0000000000..767ba77130 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; + +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import com.fhs.core.trans.anno.Trans; +import com.fhs.core.trans.constant.TransType; +import com.fhs.core.trans.vo.VO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA固件 Response VO") +public class IotOtaFirmwareRespVO implements VO { + + /** + * 固件编号 + */ + @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + private Long id; + /** + * 固件名称 + */ + @Schema(description = "固件名称", requiredMode = REQUIRED, example = "OTA固件") + private String name; + /** + * 固件描述 + */ + @Schema(description = "固件描述") + private String description; + /** + * 版本号 + */ + @Schema(description = "版本号", requiredMode = REQUIRED, example = "1.0.0") + private String version; + + /** + * 产品编号 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} + */ + @Schema(description = "产品编号", requiredMode = REQUIRED, example = "1024") + @Trans(type = TransType.SIMPLE, target = IotProductDO.class, fields = {"name"}, refs = {"productName"}) + private String productId; + /** + * 产品标识 + *

+ * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()} + */ + @Schema(description = "产品标识", requiredMode = REQUIRED, example = "iot-product-key") + private String productKey; + /** + * 产品名称 + */ + @Schema(description = "产品名称", requiredMode = REQUIRED, example = "OTA产品") + private String productName; + /** + * 签名方式 + *

+ * 例如说:MD5、SHA256 + */ + @Schema(description = "签名方式", example = "MD5") + private String signMethod; + /** + * 固件文件签名 + */ + @Schema(description = "固件文件签名", example = "1024") + private String fileSign; + /** + * 固件文件大小 + */ + @Schema(description = "固件文件大小", requiredMode = REQUIRED, example = "1024") + private Long fileSize; + /** + * 固件文件 URL + */ + @Schema(description = "固件文件 URL", requiredMode = REQUIRED, example = "https://www.iocoder.cn") + private String fileUrl; + /** + * 自定义信息,建议使用 JSON 格式 + */ + @Schema(description = "自定义信息,建议使用 JSON 格式") + private String information; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java new file mode 100644 index 0000000000..eacbfd2dd7 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA固件更新 Request VO") +public class IotOtaFirmwareUpdateReqVO extends IotOtaFirmwareCommonReqVO { + + /** + * 固件编号 + */ + @NotNull(message = "固件编号不能为空") + @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + private Long id; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java new file mode 100644 index 0000000000..c7c55e4cab --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA升级记录分页 Request VO") +public class IotOtaUpgradeRecordPageReqVO extends PageParam { + + /** + * 待处理状态 + */ + public static final Integer PENDING = 0; + /** + * 已推送状态 + */ + public static final Integer PUSHED = 10; + /** + * 正在升级状态 + */ + public static final Integer UPGRADING = 20; + /** + * 升级成功状态 + */ + public static final Integer SUCCESS = 30; + /** + * 升级失败状态 + */ + public static final Integer FAILURE = 40; + /** + * 升级已取消状态 + */ + public static final Integer CANCELED = 50; + + /** + * 升级任务编号字段。 + *

+ * 该字段用于标识升级任务的唯一编号,不能为空。 + */ + @NotNull(message = "升级任务编号不能为空") + @Schema(description = "升级任务编号", requiredMode = REQUIRED, example = "1024") + private Long taskId; + + /** + * 设备标识字段。 + *

+ * 该字段用于标识设备的名称,通常用于区分不同的设备。 + */ + @Schema(description = "设备标识", requiredMode = REQUIRED, example = "摄像头A1-1") + private String deviceName; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java new file mode 100644 index 0000000000..d717cfd310 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record; + +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import com.fhs.core.trans.anno.Trans; +import com.fhs.core.trans.constant.TransType; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA升级记录 Response VO") +public class IotOtaUpgradeRecordRespVO { + + /** + * 升级记录编号 + */ + @Schema(description = "升级记录编号", requiredMode = REQUIRED, example = "1024") + private Long id; + /** + * 固件编号 + *

+ * 关联 {@link IotOtaFirmwareDO#getId()} + */ + @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + @Trans(type = TransType.SIMPLE, target = IotOtaFirmwareDO.class, fields = {"version"}, refs = {"firmwareVersion"}) + private Long firmwareId; + /** + * 固件版本 + */ + @Schema(description = "固件版本", requiredMode = REQUIRED, example = "v1.0.0") + private String firmwareVersion; + /** + * 任务编号 + *

+ * 关联 {@link IotOtaUpgradeTaskDO#getId()} + */ + @Schema(description = "任务编号", requiredMode = REQUIRED, example = "1024") + private Long taskId; + /** + * 产品标识 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} + */ + @Schema(description = "产品标识", requiredMode = REQUIRED, example = "iot") + private String productKey; + /** + * 设备名称 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} + */ + @Schema(description = "设备名称", requiredMode = REQUIRED, example = "iot") + private String deviceName; + /** + * 设备编号 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} + */ + @Schema(description = "设备编号", requiredMode = REQUIRED, example = "1024") + private String deviceId; + /** + * 来源的固件编号 + *

+ * 关联 {@link IotDeviceDO#getFirmwareId()} + */ + @Schema(description = "来源的固件编号", requiredMode = REQUIRED, example = "1024") + @Trans(type = TransType.SIMPLE, target = IotOtaFirmwareDO.class, fields = {"version"}, refs = {"fromFirmwareVersion"}) + private Long fromFirmwareId; + /** + * 来源的固件版本 + */ + @Schema(description = "来源的固件版本", requiredMode = REQUIRED, example = "v1.0.0") + private String fromFirmwareVersion; + /** + * 升级状态 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} + */ + @Schema(description = "升级状态", requiredMode = REQUIRED, allowableValues = {"0", "10", "20", "30", "40", "50"}) + private Integer status; + /** + * 升级进度,百分比 + */ + @Schema(description = "升级进度,百分比", requiredMode = REQUIRED, example = "10") + private Integer progress; + /** + * 升级进度描述 + *

+ * 注意,只记录设备最后一次的升级进度描述 + * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志 + */ + @Schema(description = "升级进度描述", requiredMode = REQUIRED, example = "10") + private String description; + /** + * 升级开始时间 + */ + @Schema(description = "升级开始时间", requiredMode = REQUIRED, example = "2022-07-08 07:30:00") + private LocalDateTime startTime; + /** + * 升级结束时间 + */ + @Schema(description = "升级结束时间", requiredMode = REQUIRED, example = "2022-07-08 07:30:00") + private LocalDateTime endTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java new file mode 100644 index 0000000000..c1f2816c06 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA升级任务分页 Request VO") +public class IotOtaUpgradeTaskPageReqVO extends PageParam { + + /** + * 任务名称字段,用于描述任务的名称 + */ + @Schema(description = "任务名称", example = "升级任务") + private String name; + + /** + * 固件编号字段,用于唯一标识固件,不能为空 + */ + @NotNull(message = "固件编号不能为空") + @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + private Long firmwareId; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java new file mode 100644 index 0000000000..f8f5320c9d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task; + +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import com.fhs.core.trans.vo.VO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA升级任务 Response VO") +public class IotOtaUpgradeTaskRespVO implements VO { + + /** + * 任务编号 + */ + @Schema(description = "任务编号", requiredMode = REQUIRED, example = "1024") + private Long id; + /** + * 任务名称 + */ + @Schema(description = "任务名称", requiredMode = REQUIRED, example = "升级任务") + private String name; + /** + * 任务描述 + */ + @Schema(description = "任务描述", example = "升级任务") + private String description; + /** + * 固件编号 + *

+ * 关联 {@link IotOtaFirmwareDO#getId()} + */ + @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + private Long firmwareId; + /** + * 任务状态 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum} + */ + @Schema(description = "任务状态", requiredMode = REQUIRED, allowableValues = {"10", "20", "21", "30"}) + private Integer status; + /** + * 任务状态名称 + */ + @Schema(description = "任务状态名称", requiredMode = REQUIRED, example = "进行中") + private String statusName; + /** + * 升级范围 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum} + */ + @Schema(description = "升级范围", requiredMode = REQUIRED, allowableValues = {"1", "2"}) + private Integer scope; + /** + * 设备数量 + */ + @Schema(description = "设备数量", requiredMode = REQUIRED, example = "1024") + private Long deviceCount; + /** + * 选中的设备编号数组 + *

+ * 关联 {@link IotDeviceDO#getId()} + */ + @Schema(description = "选中的设备编号数组", example = "1024") + private List deviceIds; + /** + * 选中的设备名字数组 + *

+ * 关联 {@link IotDeviceDO#getDeviceName()} + */ + @Schema(description = "选中的设备名字数组", example = "1024") + private List deviceNames; + /** + * 创建时间 + */ + @Schema(description = "创建时间", requiredMode = REQUIRED, example = "2022-07-08 07:30:00") + private LocalDateTime createTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java new file mode 100644 index 0000000000..c7da7565fc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Schema(description = "管理后台 - OTA升级任务创建/修改 Request VO") +public class IotOtaUpgradeTaskSaveReqVO { + + /** + * 任务名称 + */ + @NotEmpty(message = "任务名称不能为空") + @Schema(description = "任务名称", requiredMode = REQUIRED, example = "升级任务") + private String name; + + /** + * 任务描述 + */ + @Schema(description = "任务描述", example = "升级任务") + private String description; + + /** + * 固件编号 + *

+ * 关联 {@link IotOtaFirmwareDO#getId()} + */ + @NotNull(message = "固件编号不能为空") + @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + private Long firmwareId; + + /** + * 升级范围 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum} + */ + @NotNull(message = "升级范围不能为空") + @InEnum(value = IotOtaUpgradeTaskScopeEnum.class) + @Schema(description = "升级范围", requiredMode = REQUIRED, example = "1") + private Integer scope; + + /** + * 选中的设备编号数组 + *

+ * 关联 {@link IotDeviceDO#getId()} + */ + @Schema(description = "选中的设备编号数组", requiredMode = REQUIRED, example = "[1,2,3,4]") + private List deviceIds; + + /** + * 选中的设备名字数组 + *

+ * 关联 {@link IotDeviceDO#getDeviceName()} + */ + @Schema(description = "选中的设备名字数组", requiredMode = REQUIRED, example = "[设备1,设备2,设备3,设备4]") + private List deviceNames; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java new file mode 100644 index 0000000000..66ff07e3a2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.convert.ota; + +import cn.hutool.core.convert.Convert; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; +import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +@Mapper +public interface IotOtaUpgradeRecordConvert { + + IotOtaUpgradeRecordConvert INSTANCE = Mappers.getMapper(IotOtaUpgradeRecordConvert.class); + + default List convertBOList(IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceList) { + return deviceList.stream().map(device -> { + IotOtaUpgradeRecordCreateReqBO createReqBO = new IotOtaUpgradeRecordCreateReqBO(); + createReqBO.setFirmwareId(firmware.getId()); + createReqBO.setTaskId(upgradeTask.getId()); + createReqBO.setProductKey(device.getProductKey()); + createReqBO.setDeviceName(device.getDeviceName()); + createReqBO.setDeviceId(Convert.toStr(device.getId())); + createReqBO.setFromFirmwareId(Convert.toLong(device.getFirmwareId())); + createReqBO.setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); + createReqBO.setProgress(0); + return createReqBO; + }).toList(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java index 7d9e4425db..1f300cfccb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java @@ -41,30 +41,41 @@ public class IotOtaUpgradeTaskDO extends BaseDO { /** * 固件编号 - * + *

* 关联 {@link IotOtaFirmwareDO#getId()} */ private Long firmwareId; /** * 任务状态 - * + *

* 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum} */ private Integer status; /** * 升级范围 - * + *

* 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum} */ private Integer scope; + /** + * 设备数量 + */ + private Long deviceCount; + /** + * 选中的设备编号数组 + *

+ * 关联 {@link IotDeviceDO#getId()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List deviceIds; /** * 选中的设备名字数组 - * + *

* 关联 {@link IotDeviceDO#getDeviceName()} */ @TableField(typeHandler = JacksonTypeHandler.class) private List deviceNames; -} \ No newline at end of file +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index a3ae4e3807..e56efb8798 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -60,6 +60,10 @@ public interface IotDeviceMapper extends BaseMapperX { return selectList(IotDeviceDO::getState, state); } + default List selectListByProductId(Long productId) { + return selectList(IotDeviceDO::getProductId, productId); + } + default Long selectCountByGroupId(Long groupId) { return selectCount(new LambdaQueryWrapperX() .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java new file mode 100644 index 0000000000..36e7c61cdf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IotOtaFirmwareMapper 接口用于操作 IotOtaFirmwareDO 实体类对应的数据库表。 + * 该接口继承自 BaseMapperX,提供了基本的 CRUD 操作,并扩展了特定查询方法。 + */ +@Mapper +public interface IotOtaFirmwareMapper extends BaseMapperX { + + /** + * 根据产品ID和固件版本号查询固件信息列表。 + * + * @param productId 产品ID,用于筛选固件信息。 + * @param version 固件版本号,用于筛选固件信息。 + * @return 返回符合条件的固件信息列表。 + */ + default List selectByProductIdAndVersion(String productId, String version) { + return selectList(new LambdaQueryWrapperX() + .eq(IotOtaFirmwareDO::getProductId, productId) + .eq(IotOtaFirmwareDO::getVersion, version)); + } + + /** + * 分页查询固件信息,支持根据名称和产品ID进行筛选,并按创建时间降序排列。 + * + * @param pageReqVO 分页查询请求对象,包含分页参数和筛选条件。 + * @return 返回分页查询结果,包含符合条件的固件信息列表。 + */ + default PageResult selectPage(IotOtaFirmwarePageReqVO pageReqVO) { + return selectPage(pageReqVO, + new LambdaQueryWrapperX() + .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName()) + .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId()) + .orderByDesc(IotOtaFirmwareDO::getCreateTime)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java new file mode 100644 index 0000000000..4f990fc212 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -0,0 +1,133 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * OTA升级记录 Mapper 接口 + */ +@Mapper +public interface IotOtaUpgradeRecordMapper extends BaseMapperX { + + /** + * 根据条件查询单个OTA升级记录 + * + * @param firmwareId 固件ID,可选参数,用于筛选固件ID匹配的记录 + * @param taskId 任务ID,可选参数,用于筛选任务ID匹配的记录 + * @param deviceId 设备ID,可选参数,用于筛选设备ID匹配的记录 + * @return 返回符合条件的单个OTA升级记录,如果不存在则返回null + */ + default IotOtaUpgradeRecordDO selectByConditions(Long firmwareId, Long taskId, String deviceId) { + // 使用LambdaQueryWrapperX构建查询条件,根据传入的参数动态添加查询条件 + return selectOne(new LambdaQueryWrapperX() + .eqIfPresent(IotOtaUpgradeRecordDO::getFirmwareId, firmwareId) + .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, taskId) + .eqIfPresent(IotOtaUpgradeRecordDO::getDeviceId, deviceId)); + } + + /** + * 获取OTA升级记录的数量 + * + * @param taskId 任务ID,用于筛选特定任务的升级记录 + * @param deviceName 设备名称,用于筛选特定设备的升级记录 + * @param status 状态,用于筛选特定状态的升级记录 + * @return 返回符合条件的OTA升级记录的数量 + */ + Long getOtaUpgradeRecordCount(@Param("taskId") Long taskId, + @Param("deviceName") String deviceName, + @Param("status") Integer status); + + /** + * 获取OTA升级记录的统计信息 + * + * @param firmwareId 固件ID,用于筛选特定固件的升级记录 + * @param status 状态,用于筛选特定状态的升级记录 + * @return 返回符合条件的OTA升级记录的统计信息 + */ + Long getOtaUpgradeRecordStatistics(@Param("firmwareId") Long firmwareId, + @Param("status") Integer status); + + + /** + * 根据分页查询条件获取IOT OTA升级记录的分页结果 + * + * @param pageReqVO 分页查询请求参数,包含设备名称、任务ID等查询条件 + * @return 返回分页查询结果,包含符合条件的IOT OTA升级记录列表 + */ + default PageResult selectUpgradeRecordPage(IotOtaUpgradeRecordPageReqVO pageReqVO) { + // 使用LambdaQueryWrapperX构建查询条件,并根据请求参数动态添加查询条件 + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotOtaUpgradeRecordDO::getDeviceName, pageReqVO.getDeviceName()) // 如果设备名称存在,则添加模糊查询条件 + .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, pageReqVO.getTaskId())); // 如果任务ID存在,则添加等值查询条件 + } + + /** + * 根据任务ID取消升级记录。 + * 该方法通过任务ID查找状态为“待处理”的升级记录,并将其状态更新为“已取消”。 + * + * @param taskId 任务ID,用于查找对应的升级记录。 + */ + default void cancelUpgradeRecordByTaskId(Long taskId) { + // 使用LambdaUpdateWrapper构建更新条件,将状态为“待处理”的记录更新为“已取消” + update(new LambdaUpdateWrapper() + .set(IotOtaUpgradeRecordDO::getStatus, IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()) + .eq(IotOtaUpgradeRecordDO::getTaskId, taskId) + .eq(IotOtaUpgradeRecordDO::getStatus, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()) + ); + } + + /** + * 根据状态查询符合条件的升级记录列表 + *

+ * 该函数使用LambdaQueryWrapperX构建查询条件,查询指定状态的升级记录。 + * + * @param state 升级记录的状态,用于筛选符合条件的记录 + * @return 返回符合指定状态的升级记录列表,类型为List + */ + default List selectUpgradeRecordListByState(Integer state) { + // 使用LambdaQueryWrapperX构建查询条件,根据状态查询符合条件的升级记录 + return selectList(new LambdaQueryWrapperX() + .eq(IotOtaUpgradeRecordDO::getStatus, state)); + } + + /** + * 更新升级记录状态 + *

+ * 该函数用于批量更新指定ID列表中的升级记录状态。通过传入的ID列表和状态值,使用LambdaUpdateWrapper构建更新条件, + * 并执行更新操作。 + * + * @param ids 需要更新的升级记录ID列表,类型为List。传入的ID列表中的记录将被更新。 + * @param status 要更新的状态值,类型为Integer。该值将被设置到符合条件的升级记录中。 + */ + default void updateUpgradeRecordStatus(List ids, Integer status) { + // 使用LambdaUpdateWrapper构建更新条件,设置状态字段,并根据ID列表进行筛选 + update(new LambdaUpdateWrapper() + .set(IotOtaUpgradeRecordDO::getStatus, status) + .in(IotOtaUpgradeRecordDO::getId, ids) + ); + } + + /** + * 根据任务ID查询升级记录列表 + *

+ * 该函数通过任务ID查询符合条件的升级记录,并返回查询结果列表。 + * + * @param taskId 任务ID,用于筛选升级记录 + * @return 返回符合条件的升级记录列表,若未找到则返回空列表 + */ + default List selectUpgradeRecordListByTaskId(Long taskId) { + // 使用LambdaQueryWrapperX构建查询条件,根据任务ID查询符合条件的升级记录 + return selectList(new LambdaQueryWrapperX() + .eq(IotOtaUpgradeRecordDO::getTaskId, taskId)); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java new file mode 100644 index 0000000000..9cee6e19a8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.iot.dal.mysql.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * IotOtaUpgradeTaskMapper 接口用于操作 IotOtaUpgradeTaskDO 数据库表。 + * 该接口继承自 BaseMapperX,提供了基本的数据库操作方法。 + */ +@Mapper +public interface IotOtaUpgradeTaskMapper extends BaseMapperX { + + /** + * 根据固件ID和任务名称查询升级任务列表。 + * + * @param firmwareId 固件ID,用于筛选升级任务 + * @param name 任务名称,用于筛选升级任务 + * @return 符合条件的升级任务列表 + */ + default List selectByFirmwareIdAndName(Long firmwareId, String name) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(IotOtaUpgradeTaskDO::getFirmwareId, firmwareId) + .eqIfPresent(IotOtaUpgradeTaskDO::getName, name)); + } + + /** + * 分页查询升级任务列表,支持根据固件ID和任务名称进行筛选。 + * + * @param pageReqVO 分页查询请求对象,包含分页参数和筛选条件 + * @return 分页结果,包含符合条件的升级任务列表 + */ + default PageResult selectUpgradeTaskPage(IotOtaUpgradeTaskPageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotOtaUpgradeTaskDO::getFirmwareId, pageReqVO.getFirmwareId()) + .likeIfPresent(IotOtaUpgradeTaskDO::getName, pageReqVO.getName())); + } + + /** + * 根据任务状态查询升级任务列表 + *

+ * 该函数通过传入的任务状态,查询数据库中符合条件的升级任务列表。 + * + * @param state 任务状态,用于筛选升级任务的状态值 + * @return 返回符合条件的升级任务列表,列表中的每个元素为 IotOtaUpgradeTaskDO 对象 + */ + default List selectUpgradeTaskByState(Integer state) { + // 使用 LambdaQueryWrapperX 构建查询条件,筛选出状态等于指定值的升级任务 + return selectList(new LambdaQueryWrapperX() + .eq(IotOtaUpgradeTaskDO::getStatus, state)); + } + + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java new file mode 100644 index 0000000000..d04818d5c5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.job.ota; + +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; +import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeRecordService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class IotOtaUpgradeRecordJob implements JobHandler { + + @Resource + private IotOtaUpgradeRecordService upgradeRecordService; + + @Override + @TenantJob + public String execute(String param) throws Exception { + // 1.查询待处理的升级记录 + List upgradeRecords = upgradeRecordService + .getUpgradeRecordListByState(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); + + // TODO @芋艿 2.执行升级动作 + + // 3.最终,更新升级记录状态 + List ids = upgradeRecords.stream().map(IotOtaUpgradeRecordDO::getId).toList(); + upgradeRecordService.updateUpgradeRecordStatus(ids, IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); + return ""; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java new file mode 100644 index 0000000000..8b809c674b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.iot.job.ota; + +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum; +import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeRecordService; +import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeTaskService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +public class IotOtaUpgradeTaskJob implements JobHandler { + + @Resource + private IotOtaUpgradeTaskService upgradeTaskService; + @Resource + private IotOtaUpgradeRecordService upgradeRecordService; + + @Override + @TenantJob + public String execute(String param) throws Exception { + // 1.这个任务主要是为了检查并更新升级任务的状态 + List upgradeTasks = upgradeTaskService + .getUpgradeTaskByState(IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus()); + // 2.遍历并且确定升级任务的状态 + upgradeTasks.forEach(this::checkUpgradeTaskState); + // TODO @芋艿: 其他的一些业务逻辑 + return ""; + } + + private void checkUpgradeTaskState(IotOtaUpgradeTaskDO upgradeTask) { + // 1.查询任务所有的升级记录 + List upgradeRecords = + upgradeRecordService.getUpgradeRecordListByTaskId(upgradeTask.getId()); + if (upgradeRecords.stream().anyMatch(upgradeRecord -> + ObjectUtils.equalsAny(upgradeRecord.getStatus(), + IotOtaUpgradeRecordStatusEnum.PENDING.getStatus(), + IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus(), + IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()))) { + // 如果存在正在升级的升级记录,则升级任务的状态为进行中 + log.debug("升级任务 {} 状态为进行中", upgradeTask.getId()); + } else if (upgradeRecords.stream().allMatch(upgradeRecord -> + ObjectUtils.equalsAny(upgradeRecord.getStatus(), + IotOtaUpgradeRecordStatusEnum.SUCCESS.getStatus()))) { + // 如果全部升级成功,则升级任务的状态为已完成 + upgradeTaskService.updateUpgradeTaskStatus(upgradeTask.getId(), + IotOtaUpgradeTaskStatusEnum.COMPLETED.getStatus()); + } else if (upgradeRecords.stream().noneMatch(upgradeRecord -> + ObjectUtils.equalsAny(upgradeRecord.getStatus(), + IotOtaUpgradeRecordStatusEnum.PENDING.getStatus(), + IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus(), + IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus())) && + upgradeRecords.stream().anyMatch(upgradeRecord -> + ObjectUtils.equalsAny(upgradeRecord.getStatus(), + IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus(), + IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()))) { + // 如果全部升级完毕,但是存在升级失败或者取消的升级记录,则升级任务的状态为失败 + upgradeTaskService.updateUpgradeTaskStatus(upgradeTask.getId(), + IotOtaUpgradeTaskStatusEnum.INCOMPLETE.getStatus()); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 24532d2548..cd920d1bf1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -34,8 +34,8 @@ public interface IotDeviceService { * @return 设备 */ IotDeviceDO createDevice(@NotEmpty(message = "产品标识不能为空") String productKey, - @NotEmpty(message = "设备名称不能为空") String deviceName, - Long gatewayId); + @NotEmpty(message = "设备名称不能为空") String deviceName, + Long gatewayId); /** * 更新设备 @@ -45,6 +45,7 @@ public interface IotDeviceService { void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); // TODO @芋艿:先这么实现。未来看情况,要不要自己实现 + /** * 更新设备的所属网关 * @@ -132,6 +133,22 @@ public interface IotDeviceService { */ List getDeviceListByState(Integer state); + /** + * 根据产品ID获取设备列表 + * + * @param productId 产品ID,用于查询特定产品的设备列表 + * @return 返回与指定产品ID关联的设备列表,列表中的每个元素为IotDeviceDO对象 + */ + List getDeviceListByProductId(Long productId); + + /** + * 根据设备ID列表获取设备信息列表 + * + * @param deviceIdList 设备ID列表,包含需要查询的设备ID + * @return 返回与设备ID列表对应的设备信息列表,列表中的每个元素为IotDeviceDO对象 + */ + List getDeviceListByIdList(List deviceIdList); + /** * 基于产品编号,获得设备数量 * @@ -150,7 +167,7 @@ public interface IotDeviceService { /** * 【缓存】根据产品 key 和设备名称,获得设备信息 - * + *

* 注意:该方法会忽略租户信息,所以调用时,需要确认会不会有跨租户访问的风险!!! * * @param productKey 产品 key diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 66854e84b8..76d16c538c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -99,7 +99,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { } private void validateCreateDeviceParam(String productKey, String deviceName, String deviceKey, - Long gatewayId, IotProductDO product) { + Long gatewayId, IotProductDO product) { TenantUtils.executeIgnore(() -> { // 校验设备名称在同一产品下是否唯一 if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) { @@ -261,6 +261,16 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectListByState(state); } + @Override + public List getDeviceListByProductId(Long productId) { + return deviceMapper.selectListByProductId(productId); + } + + @Override + public List getDeviceListByIdList(List deviceIdList) { + return deviceMapper.selectByIds(deviceIdList); + } + @Override public void updateDeviceState(Long id, Integer state) { // 1. 校验存在 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java new file mode 100644 index 0000000000..659b3039fc --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.service.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import jakarta.validation.Valid; + +/** + * OTA固件管理服务接口 + * 提供OTA固件的创建、更新和查询等功能 + */ +public interface IotOtaFirmwareService { + + /** + * 创建OTA固件 + * + * @param saveReqVO OTA固件保存请求对象,包含固件的相关信息 + * @return 返回新创建的固件的ID + */ + Long createOtaFirmware(@Valid IotOtaFirmwareCreateReqVO saveReqVO); + + /** + * 更新OTA固件信息 + * + * @param updateReqVO OTA固件保存请求对象,包含需要更新的固件信息 + */ + void updateOtaFirmware(@Valid IotOtaFirmwareUpdateReqVO updateReqVO); + + /** + * 根据ID获取OTA固件信息 + * + * @param id OTA固件的唯一标识符 + * @return 返回OTA固件的详细信息对象 + */ + IotOtaFirmwareDO getOtaFirmware(Long id); + + /** + * 分页查询OTA固件信息 + * + * @param pageReqVO 包含分页查询条件的请求对象 + * @return 返回分页查询结果,包含固件信息列表和分页信息 + */ + PageResult getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO); + + /** + * 验证物联网OTA固件是否存在 + * + * @param id 固件的唯一标识符 + * 该方法用于检查系统中是否存在与给定ID关联的物联网OTA固件信息 + * 主要目的是在进行固件更新操作前,确保目标固件已经存在并可以被访问 + * 如果固件不存在,该方法可能抛出异常或返回错误信息,具体行为未定义 + */ + IotOtaFirmwareDO validateFirmwareExists(Long id); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java new file mode 100644 index 0000000000..76930e1dc6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.iot.service.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaFirmwareMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_NOT_EXISTS; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE; + +@Slf4j +@Service +@Validated +public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { + + @Resource + private IotOtaFirmwareMapper otaFirmwareMapper; + + @Override + public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) { + // 1.校验固件产品id+版本号不能重复 + validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion()); + // 2.转化数据格式,准备存储到数据库中 + IotOtaFirmwareDO otaFirmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); + otaFirmwareMapper.insert(otaFirmware); + return otaFirmware.getId(); + } + + @Override + public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) { + // 1.1. 校验存在 + validateFirmwareExists(updateReqVO.getId()); + // 2. 更新数据 + IotOtaFirmwareDO updateObj = BeanUtils.toBean(updateReqVO, IotOtaFirmwareDO.class); + otaFirmwareMapper.updateById(updateObj); + } + + @Override + public IotOtaFirmwareDO getOtaFirmware(Long id) { + return otaFirmwareMapper.selectById(id); + } + + @Override + public PageResult getOtaFirmwarePage(IotOtaFirmwarePageReqVO pageReqVO) { + return otaFirmwareMapper.selectPage(pageReqVO); + } + + @Override + public IotOtaFirmwareDO validateFirmwareExists(Long id) { + IotOtaFirmwareDO otaFirmware = otaFirmwareMapper.selectById(id); + if (otaFirmware == null) { + throw exception(OTA_FIRMWARE_NOT_EXISTS); + } + return otaFirmware; + } + + /** + * 验证产品和版本号是否重复 + *

+ * 该方法用于确保在系统中不存在具有相同产品ID和版本号的固件条目 + * 它通过调用otaFirmwareMapper的selectByProductIdAndVersion方法来查询数据库中是否存在匹配的产品ID和版本号的固件信息 + * 如果查询结果非空且不为null,则抛出异常,提示固件信息已存在,从而避免数据重复 + * + * @param productId 产品ID,用于数据库查询 + * @param version 版本号,用于数据库查询 + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出异常,提示固件信息已存在 + */ + private void validateProductAndVersionDuplicate(String productId, String version) { + // 查询数据库中是否存在具有相同产品ID和版本号的固件信息 + List list = otaFirmwareMapper.selectByProductIdAndVersion(productId, version); + // 如果查询结果非空且不为null,则抛出异常,提示固件信息已存在 + if (Objects.nonNull(list) && !list.isEmpty()) { + throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java new file mode 100644 index 0000000000..18bf63e83d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.iot.service.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordUpdateReqBO; +import jakarta.validation.Valid; + +import java.util.List; +import java.util.Map; + +/** + * IotOtaUpgradeRecordService 接口定义了与物联网设备OTA升级记录相关的操作。 + * 该接口提供了创建、更新、查询、统计和重试升级记录的功能。 + */ +public interface IotOtaUpgradeRecordService { + + /** + * 批量创建物联网OTA升级记录 + *

+ * 该函数用于处理一组物联网OTA升级记录的创建请求,并将这些记录批量保存到系统中。 + * + * @param saveList 包含多个物联网OTA升级记录创建请求的列表,每个请求对象都经过校验(@Valid注解确保) + * 列表中的每个元素都是IotOtaUpgradeRecordCreateReqBO类型的对象,表示一个独立的升级记录创建请求。 + */ + void createUpgradeRecordBatch(@Valid List saveList); + + /** + * 更新现有的OTA升级记录。 + * + * @param updateReqBO 包含更新升级记录所需信息的请求对象,必须经过验证。 + */ + void updateUpgradeRecord(@Valid IotOtaUpgradeRecordUpdateReqBO updateReqBO); + + /** + * 获取OTA升级记录的数量统计。 + * + * @return 返回一个Map,其中键为状态码,值为对应状态的升级记录数量。 + */ + Map getOtaUpgradeRecordCount(@Valid IotOtaUpgradeRecordPageReqVO pageReqVO); + + /** + * 获取OTA升级记录的统计信息。 + * + * @return 返回一个Map,其中键为状态码,值为对应状态的升级记录统计信息。 + */ + Map getOtaUpgradeRecordStatistics(Long firmwareId); + + /** + * 重试指定的OTA升级记录。 + * + * @param id 需要重试的升级记录的ID。 + */ + void retryUpgradeRecord(Long id); + + /** + * 获取指定ID的OTA升级记录的详细信息。 + * + * @param id 需要查询的升级记录的ID。 + * @return 返回包含升级记录详细信息的响应对象。 + */ + IotOtaUpgradeRecordDO getUpgradeRecord(Long id); + + /** + * 分页查询OTA升级记录。 + * + * @param pageReqVO 包含分页查询条件的请求对象,必须经过验证。 + * @return 返回包含分页查询结果的响应对象。 + */ + PageResult getUpgradeRecordPage( + @Valid IotOtaUpgradeRecordPageReqVO pageReqVO); + + /** + * 根据任务ID取消升级记录。 + *

+ * 该函数用于根据给定的任务ID,取消与该任务相关的升级记录。通常用于在任务执行失败或用户手动取消时, + * 清理或标记相关的升级记录为取消状态。 + * + * @param taskId 要取消升级记录的任务ID。该ID唯一标识一个任务,通常由任务管理系统生成。 + */ + void cancelUpgradeRecordByTaskId(Long taskId); + + /** + * 根据升级状态获取升级记录列表 + * + * @param state 升级状态,用于筛选符合条件的升级记录 + * @return 返回符合指定状态的升级记录列表,列表中的元素为 {@link IotOtaUpgradeRecordDO} 对象 + */ + List getUpgradeRecordListByState(Integer state); + + /** + * 更新升级记录的状态。 + *

+ * 该函数用于批量更新指定升级记录的状态。通过传入的ID列表和状态值,将对应的升级记录的状态更新为指定的值。 + * + * @param ids 需要更新状态的升级记录的ID列表。列表中的每个元素代表一个升级记录的ID。 + * @param status 要更新的状态值。该值应为有效的状态标识符,通常为整数类型。 + */ + void updateUpgradeRecordStatus(List ids, Integer status); + + /** + * 根据任务ID获取升级记录列表 + *

+ * 该函数通过给定的任务ID,查询并返回与该任务相关的所有升级记录。 + * + * @param taskId 任务ID,用于指定需要查询的任务 + * @return 返回一个包含升级记录的列表,列表中的每个元素为IotOtaUpgradeRecordDO对象 + */ + List getUpgradeRecordListByTaskId(Long taskId); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java new file mode 100644 index 0000000000..71168cd2d2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java @@ -0,0 +1,207 @@ +package cn.iocoder.yudao.module.iot.service.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeRecordMapper; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; +import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordUpdateReqBO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +@Slf4j +@Service +@Validated +public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordService { + + @Resource + private IotOtaUpgradeRecordMapper upgradeRecordMapper; + + @Override + public void createUpgradeRecordBatch(List saveList) { + // 1. 批量校验参数信息 + saveList.forEach(saveBO -> validateUpgradeRecordDuplicate(saveBO.getFirmwareId(), saveBO.getTaskId(), saveBO.getDeviceId())); + // 2.将数据转化成数据库存储的格式 + List upgradeRecords = BeanUtils.toBean(saveList, IotOtaUpgradeRecordDO.class); + // 3.将数据批量存储到数据库里 + upgradeRecordMapper.insertBatch(upgradeRecords); + } + + @Override + @Transactional + public void updateUpgradeRecord(IotOtaUpgradeRecordUpdateReqBO updateReqBO) { + // 1.校验升级记录信息是否存在 + validateUpgradeRecordExists(updateReqBO.getId()); + // 2.将数据转化成数据库存储的格式 + IotOtaUpgradeRecordDO updateRecord = BeanUtils.toBean(updateReqBO, IotOtaUpgradeRecordDO.class); + upgradeRecordMapper.updateById(updateRecord); + // TODO @芋艿: 更新升级记录触发的其他Action + } + + /** + * 获取OTA升级记录的数量统计。 + * 该方法根据传入的查询条件,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。 + * + * @param pageReqVO 包含查询条件的请求对象,主要包括任务ID和设备名称等信息。 + * @return 返回一个Map,其中键为状态常量,值为对应状态的记录数量。 + */ + @Override + @Transactional + public Map getOtaUpgradeRecordCount(IotOtaUpgradeRecordPageReqVO pageReqVO) { + // 分别查询不同状态的OTA升级记录数量 + Long pending = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); + Long pushed = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); + Long upgrading = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()); + Long success = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.SUCCESS.getStatus()); + Long failure = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus()); + Long canceled = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()); + // 将各状态的数量封装到Map中返回 + return Map.of(IotOtaUpgradeRecordPageReqVO.PENDING, pending, + IotOtaUpgradeRecordPageReqVO.PUSHED, pushed, + IotOtaUpgradeRecordPageReqVO.UPGRADING, upgrading, + IotOtaUpgradeRecordPageReqVO.SUCCESS, success, + IotOtaUpgradeRecordPageReqVO.FAILURE, failure, + IotOtaUpgradeRecordPageReqVO.CANCELED, canceled); + } + + /** + * 获取指定固件ID的OTA升级记录统计信息。 + * 该方法通过查询数据库,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。 + * + * @param firmwareId 固件ID,用于指定需要统计的固件升级记录。 + * @return 返回一个Map,其中键为升级记录状态(如PENDING、PUSHED等),值为对应状态的记录数量。 + */ + @Override + @Transactional + public Map getOtaUpgradeRecordStatistics(Long firmwareId) { + // 查询并统计不同状态的OTA升级记录数量 + Long pending = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); + Long pushed = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); + Long upgrading = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()); + Long success = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.SUCCESS.getStatus()); + Long failure = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus()); + Long canceled = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()); + // 将统计结果封装为Map并返回 + return Map.of(IotOtaUpgradeRecordPageReqVO.PENDING, pending, + IotOtaUpgradeRecordPageReqVO.PUSHED, pushed, + IotOtaUpgradeRecordPageReqVO.UPGRADING, upgrading, + IotOtaUpgradeRecordPageReqVO.SUCCESS, success, + IotOtaUpgradeRecordPageReqVO.FAILURE, failure, + IotOtaUpgradeRecordPageReqVO.CANCELED, canceled); + } + + @Override + public void retryUpgradeRecord(Long id) { + // 1.1.校验升级记录信息是否存在 + IotOtaUpgradeRecordDO upgradeRecord = validateUpgradeRecordExists(id); + // 1.2.校验升级记录是否可以重新升级 + validateUpgradeRecordCanRetry(upgradeRecord); + // 2.将一些数据重置,这样定时任务轮询就可以重启任务 + upgradeRecordMapper.updateById(new IotOtaUpgradeRecordDO() + .setId(upgradeRecord.getId()).setProgress(0) + .setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus())); + // TODO @芋艿: 重试升级记录触发的其他Action + // TODO 如果一个升级记录被取消或者已经执行失败,重试成功,是否会对升级任务的状态有影响? + } + + @Override + public IotOtaUpgradeRecordDO getUpgradeRecord(Long id) { + return upgradeRecordMapper.selectById(id); + } + + @Override + public PageResult getUpgradeRecordPage(IotOtaUpgradeRecordPageReqVO pageReqVO) { + return upgradeRecordMapper.selectUpgradeRecordPage(pageReqVO); + } + + @Override + public void cancelUpgradeRecordByTaskId(Long taskId) { + // 暂定只有待推送的升级记录可以取消 + upgradeRecordMapper.cancelUpgradeRecordByTaskId(taskId); + } + + @Override + public List getUpgradeRecordListByState(Integer state) { + return upgradeRecordMapper.selectUpgradeRecordListByState(state); + } + + @Override + public void updateUpgradeRecordStatus(List ids, Integer status) { + upgradeRecordMapper.updateUpgradeRecordStatus(ids, status); + } + + @Override + public List getUpgradeRecordListByTaskId(Long taskId) { + return upgradeRecordMapper.selectUpgradeRecordListByTaskId(taskId); + } + + /** + * 验证指定的升级记录是否存在。 + *

+ * 该函数通过给定的ID查询升级记录,如果查询结果为空,则抛出异常,表示升级记录不存在。 + * + * @param id 升级记录的唯一标识符,类型为Long。 + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出异常,异常类型为OTA_UPGRADE_RECORD_NOT_EXISTS。 + */ + private IotOtaUpgradeRecordDO validateUpgradeRecordExists(Long id) { + // 根据ID查询升级记录 + IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordMapper.selectById(id); + // 如果查询结果为空,抛出异常 + if (upgradeRecord == null) { + throw exception(OTA_UPGRADE_RECORD_NOT_EXISTS); + } + return upgradeRecord; + } + + /** + * 验证固件升级记录是否存在。 + *

+ * 该函数通过给定的固件ID、任务ID和设备ID查询升级记录,如果查询结果为空,则抛出异常。 + * + * @param firmwareId 固件ID,用于标识特定的固件版本 + * @param taskId 任务ID,用于标识特定的升级任务 + * @param deviceId 设备ID,用于标识特定的设备 + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出OTA_UPGRADE_RECORD_NOT_EXISTS异常 + */ + private void validateUpgradeRecordDuplicate(Long firmwareId, Long taskId, String deviceId) { + // 根据条件查询升级记录 + IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordMapper.selectByConditions(firmwareId, taskId, deviceId); + // 如果查询结果为空,抛出异常 + if (upgradeRecord != null) { + throw exception(OTA_UPGRADE_RECORD_DUPLICATE); + } + } + + /** + * 验证升级记录是否可以重试。 + *

+ * 该方法用于检查给定的升级记录是否处于允许重试的状态。如果升级记录的状态为 + * PENDING、PUSHED 或 UPGRADING,则抛出异常,表示不允许重试。 + * + * @param upgradeRecord 需要验证的升级记录对象,类型为 IotOtaUpgradeRecordDO + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出 OTA_UPGRADE_RECORD_CANNOT_RETRY 异常 + */ + private void validateUpgradeRecordCanRetry(IotOtaUpgradeRecordDO upgradeRecord) { + // 检查升级记录的状态是否为 PENDING、PUSHED 或 UPGRADING + if (ObjectUtils.equalsAny(upgradeRecord.getStatus(), + IotOtaUpgradeRecordStatusEnum.PENDING.getStatus(), + IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus(), + IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus())) { + // 如果升级记录处于上述状态之一,则抛出异常,表示不允许重试 + throw exception(OTA_UPGRADE_RECORD_CANNOT_RETRY); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java new file mode 100644 index 0000000000..5b3444214c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.iot.service.ota; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * IoT OTA升级任务服务接口 + * 提供OTA升级任务的创建、取消和查询功能 + */ +public interface IotOtaUpgradeTaskService { + + /** + * 创建OTA升级任务 + * + * @param createReqVO OTA升级任务的创建请求对象,包含创建任务所需的信息 + * @return 创建成功的OTA升级任务的ID + */ + Long createUpgradeTask(@Valid IotOtaUpgradeTaskSaveReqVO createReqVO); + + /** + * 取消OTA升级任务 + * + * @param id 要取消的OTA升级任务的ID + */ + void cancelUpgradeTask(Long id); + + /** + * 根据ID获取OTA升级任务的详细信息 + * + * @param id OTA升级任务的ID + * @return OTA升级任务的详细信息对象 + */ + IotOtaUpgradeTaskDO getUpgradeTask(Long id); + + /** + * 分页查询OTA升级任务 + * + * @param pageReqVO OTA升级任务的分页查询请求对象,包含查询条件和分页信息 + * @return 分页查询结果,包含OTA升级任务列表和总记录数 + */ + PageResult getUpgradeTaskPage(@Valid IotOtaUpgradeTaskPageReqVO pageReqVO); + + /** + * 根据任务状态获取升级任务列表 + * + * @param state 任务状态,用于筛选符合条件的升级任务 + * @return 返回符合指定状态的升级任务列表,列表中的元素为 IotOtaUpgradeTaskDO 对象 + */ + List getUpgradeTaskByState(Integer state); + + /** + * 更新升级任务的状态。 + *

+ * 该函数用于根据任务ID更新指定升级任务的状态。通常用于在任务执行过程中 + * 更新任务的状态,例如从“进行中”变为“已完成”或“失败”。 + * + * @param id 升级任务的唯一标识符,类型为Long。不能为null。 + * @param status 要更新的任务状态,类型为Integer。通常表示任务的状态码,如0表示未开始,1表示进行中,2表示已完成等。 + */ + void updateUpgradeTaskStatus(Long id, Integer status); + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java new file mode 100644 index 0000000000..b66ae402c8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java @@ -0,0 +1,236 @@ +package cn.iocoder.yudao.module.iot.service.ota; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO; +import cn.iocoder.yudao.module.iot.convert.ota.IotOtaUpgradeRecordConvert; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeTaskMapper; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; + +@Slf4j +@Service +@Validated +public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { + + @Resource + private IotOtaUpgradeTaskMapper upgradeTaskMapper; + @Lazy + @Resource + private IotDeviceService deviceService; + @Lazy + @Resource + private IotOtaFirmwareService firmwareService; + @Lazy + @Resource + private IotOtaUpgradeRecordService upgradeRecordService; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO) { + // 1.1.校验同一固件的升级任务名称不重复 + validateFirmwareTaskDuplicate(createReqVO.getFirmwareId(), createReqVO.getName()); + // 1.2.校验固件信息是否存在 + IotOtaFirmwareDO firmware = firmwareService.validateFirmwareExists(createReqVO.getFirmwareId()); + // 1.3.校验升级范围=2(指定设备时),deviceIds deviceNames不为空并且长度相等 + validateScopeAndDevice(createReqVO.getScope(), createReqVO.getDeviceIds(), createReqVO.getDeviceNames()); + // 2.初始化OTA升级任务信息 + IotOtaUpgradeTaskDO upgradeTask = initUpgradeTask(createReqVO, firmware.getProductId()); + // 3.保存OTA升级任务信息到数据库 + upgradeTaskMapper.insert(upgradeTask); + // 4.生成设备升级记录信息并存储,等待定时任务轮询 + List upgradeRecordList = initUpgradeRecordList(upgradeTask, firmware, createReqVO.getDeviceIds()); + upgradeRecordService.createUpgradeRecordBatch(upgradeRecordList); + // TODO @芋艿: 创建任务触发的其他Action + return upgradeTask.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelUpgradeTask(Long id) { + // 1.1.校验升级任务是否存在 + IotOtaUpgradeTaskDO upgradeTask = validateUpgradeTaskExists(id); + // 1.2.校验升级任务是否可以取消 + validateUpgradeTaskCanCancel(upgradeTask); + // 2.更新OTA升级任务状态为已取消 + upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder() + .id(id).status(IotOtaUpgradeTaskStatusEnum.CANCELED.getStatus()) + .build()); + // 3.更新OTA升级记录状态为已取消 + upgradeRecordService.cancelUpgradeRecordByTaskId(id); + // TODO @芋艿: 取消任务触发的其他Action + } + + @Override + public IotOtaUpgradeTaskDO getUpgradeTask(Long id) { + return upgradeTaskMapper.selectById(id); + } + + @Override + public PageResult getUpgradeTaskPage(IotOtaUpgradeTaskPageReqVO pageReqVO) { + return upgradeTaskMapper.selectUpgradeTaskPage(pageReqVO); + } + + @Override + public List getUpgradeTaskByState(Integer state) { + return upgradeTaskMapper.selectUpgradeTaskByState(state); + } + + @Override + public void updateUpgradeTaskStatus(Long id, Integer status) { + upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder() + .id(id).status(status) + .build()); + } + + /** + * 校验固件升级任务是否重复 + *

+ * 该方法用于检查给定固件ID和任务名称组合是否已存在于数据库中,如果存在则抛出异常, + * 表示任务名称对于该固件而言是重复的此检查确保用户不能创建具有相同名称的任务, + * 从而避免数据重复和混淆 + * + * @param firmwareId 固件的唯一标识符,用于区分不同的固件 + * @param taskName 升级任务的名称,用于与固件ID一起检查重复性 + * @throws cn.iocoder.yudao.framework.common.exception.ServerException 则抛出预定义的异常 + */ + private void validateFirmwareTaskDuplicate(Long firmwareId, String taskName) { + // 查询数据库中是否有相同固件ID和任务名称的升级任务存在 + List upgradeTaskList = upgradeTaskMapper.selectByFirmwareIdAndName(firmwareId, taskName); + // 如果查询结果不为空,说明存在重复的任务名称,抛出异常 + if (CollUtil.isNotEmpty(upgradeTaskList)) { + throw exception(OTA_UPGRADE_TASK_NAME_DUPLICATE); + } + } + + /** + * 验证升级任务的范围和设备参数是否有效 + * 当选择特定设备进行升级时,确保提供的设备ID和设备名称列表有效且对应 + * + * @param scope 升级任务的范围,表示是选择特定设备还是其他范围 + * @param deviceIds 设备ID列表,用于标识参与升级的设备 + * @param deviceNames 设备名称列表,与设备ID列表对应 + */ + private void validateScopeAndDevice(Integer scope, List deviceIds, List deviceNames) { + // 当升级任务范围为选择特定设备时 + if (Objects.equals(scope, IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { + // 检查设备ID列表和设备名称列表是否为空或长度不一致,若不符合要求,则抛出异常 + if (CollUtil.isEmpty(deviceIds) || CollUtil.isEmpty(deviceNames) || deviceIds.size() != deviceNames.size()) { + throw exception(OTA_UPGRADE_TASK_PARAMS_INVALID); + } + } + } + + /** + * 验证升级任务是否存在 + *

+ * 通过查询数据库来验证给定ID的升级任务是否存在此方法主要用于确保后续操作所针对的升级任务是有效的 + * + * @param id 升级任务的唯一标识符如果为null或数据库中不存在对应的记录,则认为任务不存在 + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException 如果升级任务不存在,则抛出异常提示任务不存在 + */ + private IotOtaUpgradeTaskDO validateUpgradeTaskExists(Long id) { + // 查询数据库中是否有相同固件ID和任务名称的升级任务存在 + IotOtaUpgradeTaskDO upgradeTask = upgradeTaskMapper.selectById(id); + // 如果查询结果不为空,说明存在重复的任务名称,抛出异常 + if (Objects.isNull(upgradeTask)) { + throw exception(OTA_UPGRADE_TASK_NOT_EXISTS); + } + return upgradeTask; + } + + /** + * 验证升级任务是否可以被取消 + *

+ * 此方法旨在确保只有当升级任务处于进行中状态时,才可以执行取消操作 + * 它通过比较任务的当前状态与预定义的进行中状态来判断是否允许取消操作 + * 如果任务状态不符合条件,则抛出异常,表明该任务无法取消 + * + * @param upgradeTask 待验证的升级任务对象,包含任务的详细信息,如状态等 + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException 如果任务状态不是进行中,则抛出此异常,表明任务无法取消 + */ + private void validateUpgradeTaskCanCancel(IotOtaUpgradeTaskDO upgradeTask) { + // 检查升级任务的状态是否为进行中,只有此状态下的任务才允许取消 + if (!Objects.equals(upgradeTask.getStatus(), IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus())) { + // 只有进行中的任务才可以取消 + throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL); + } + } + + /** + * 初始化升级任务 + *

+ * 根据请求参数创建升级任务对象,并根据选择的范围初始化设备数量 + * 如果选择特定设备进行升级,则设备数量为所选设备的总数 + * 如果选择全部设备进行升级,则设备数量为该固件对应产品下的所有设备总数 + * + * @param createReqVO 升级任务保存请求对象,包含创建升级任务所需的信息 + * @return 返回初始化后的升级任务对象 + */ + private IotOtaUpgradeTaskDO initUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO, String productId) { + // 配置各项参数 + IotOtaUpgradeTaskDO upgradeTask = IotOtaUpgradeTaskDO.builder() + .name(createReqVO.getName()) + .description(createReqVO.getDescription()) + .firmwareId(createReqVO.getFirmwareId()) + .scope(createReqVO.getScope()) + .deviceIds(createReqVO.getDeviceIds()) + .deviceNames(createReqVO.getDeviceNames()) + .deviceCount(Convert.toLong(CollUtil.size(createReqVO.getDeviceIds()))) + .status(IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus()) + .build(); + // 如果选择全选,则需要查询设备数量 + if (Objects.equals(createReqVO.getScope(), IotOtaUpgradeTaskScopeEnum.ALL.getScope())) { + // 根据产品ID查询设备数量 + Long deviceCount = deviceService.getDeviceCountByProductId(Convert.toLong(productId)); + // 设置升级任务的设备数量 + upgradeTask.setDeviceCount(deviceCount); + } + // 返回初始化后的升级任务对象 + return upgradeTask; + } + + /** + * 初始化升级记录列表 + *

+ * 根据升级任务的范围(选择设备或按产品ID)获取设备列表,并将其转换为升级记录请求对象列表。 + * + * @param upgradeTask 升级任务对象,包含升级任务的相关信息 + * @param firmware 固件对象,包含固件的相关信息 + * @param deviceIds 设备ID列表,仅在升级任务范围为选择设备时使用 + * @return 升级记录请求对象列表,包含每个设备的升级记录信息 + */ + private List initUpgradeRecordList(IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceIds) { + // 根据升级任务的范围确定设备列表 + List deviceList; + if (Objects.equals(upgradeTask.getScope(), IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { + // 如果升级任务范围为选择设备,则根据设备ID列表获取设备信息 + deviceList = deviceService.getDeviceListByIdList(deviceIds); + } else { + // 如果升级任务范围为按产品ID,则根据固件的产品ID获取设备信息 + deviceList = deviceService.getDeviceListByProductId(Convert.toLong(firmware.getProductId())); + } + // 将升级任务、固件和设备列表转换为升级记录请求对象列表 + return IotOtaUpgradeRecordConvert.INSTANCE.convertBOList(upgradeTask, firmware, deviceList); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java new file mode 100644 index 0000000000..fbed390ae2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.service.ota.bo; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java new file mode 100644 index 0000000000..af729fae30 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.service.ota.bo.upgrade; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java new file mode 100644 index 0000000000..e3accc4275 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record; + +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class IotOtaUpgradeRecordCreateReqBO { + + /** + * 固件编号 + *

+ * 关联 {@link IotOtaFirmwareDO#getId()} + */ + @NotNull(message = "固件编号不能为空") + private Long firmwareId; + /** + * 任务编号 + *

+ * 关联 {@link IotOtaUpgradeTaskDO#getId()} + */ + @NotNull(message = "任务编号不能为空") + private Long taskId; + /** + * 产品标识 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} + */ + private String productKey; + /** + * 设备名称 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} + */ + private String deviceName; + /** + * 设备编号 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} + */ + @NotNull(message = "设备编号不能为空") + private String deviceId; + /** + * 来源的固件编号 + *

+ * 关联 {@link IotDeviceDO#getFirmwareId()} + */ + private Long fromFirmwareId; + /** + * 升级状态 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} + */ + private Integer status; + /** + * 升级进度,百分比 + */ + private Integer progress; + /** + * 升级进度描述 + *

+ * 注意,只记录设备最后一次的升级进度描述 + * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志 + */ + private String description; + /** + * 升级开始时间 + */ + private LocalDateTime startTime; + /** + * 升级结束时间 + */ + private LocalDateTime endTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java new file mode 100644 index 0000000000..e0e71097c9 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Data +public class IotOtaUpgradeRecordUpdateReqBO { + + /** + * 升级记录编号 + */ + @NotNull(message = "升级记录编号不能为空") + private Long id; + /** + * 升级状态 + *

+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} + */ + @InEnum(IotOtaUpgradeRecordStatusEnum.class) + private Integer status; + /** + * 升级进度,百分比 + */ + @Range(min = 0, max = 100, message = "升级进度必须介于 0-100 之间") + private Integer progress; + /** + * 升级开始时间 + */ + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + /** + * 升级结束时间 + */ + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime endTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml new file mode 100644 index 0000000000..0dac6efb5c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + \ No newline at end of file From 12170402e236ebf2ebd52d180502659c4a753a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E9=87=8E=E7=BE=A1=E6=B0=91?= Date: Thu, 20 Feb 2025 02:39:48 +0000 Subject: [PATCH 180/309] =?UTF-8?q?=E3=80=90=E6=80=A7=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E5=BE=AA=E7=8E=AF=E4=B8=AD=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=E6=9F=A5=E8=AF=A2=20update?= =?UTF-8?q?=20r/yudao/module/promotion/service/coupon/CouponServiceImpl.ja?= =?UTF-8?q?va.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 山野羡民 --- .../promotion/service/coupon/CouponServiceImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java index 060306f118..1c5db82889 100644 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java @@ -121,6 +121,10 @@ public class CouponServiceImpl implements CouponService { @Transactional(rollbackFor = Exception.class) public Map> takeCoupon(Long templateId, Set userIds, CouponTakeTypeEnum takeType) { CouponTemplateDO template = couponTemplateService.getCouponTemplate(templateId); + return takeCoupon(template, userIds, takeType); + } + + private Map> takeCoupon(CouponTemplateDO template, Set userIds, CouponTakeTypeEnum takeType) { // 1. 过滤掉达到领取限制的用户 removeTakeLimitUser(userIds, template); // 2. 校验优惠劵是否可以领取 @@ -131,7 +135,7 @@ public class CouponServiceImpl implements CouponService { couponMapper.insertBatch(couponList); // 4. 增加优惠劵模板的领取数量 - couponTemplateService.updateCouponTemplateTakeCount(templateId, userIds.size()); + couponTemplateService.updateCouponTemplateTakeCount(template.getId(), userIds.size()); return convertMultiMap(couponList, CouponDO::getUserId, CouponDO::getId); } @@ -208,7 +212,7 @@ public class CouponServiceImpl implements CouponService { public void takeCouponByRegister(Long userId) { List templates = couponTemplateService.getCouponTemplateListByTakeType(CouponTakeTypeEnum.REGISTER); for (CouponTemplateDO template : templates) { - takeCoupon(template.getId(), CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER); + takeCoupon(template, CollUtil.newHashSet(userId), CouponTakeTypeEnum.REGISTER); } } From db9534073b0b05a46395c0532a43df00f879401c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B1=B1=E9=87=8E=E7=BE=A1=E6=B0=91?= Date: Thu, 20 Feb 2025 02:43:13 +0000 Subject: [PATCH 181/309] =?UTF-8?q?=E3=80=90bugfix=E3=80=91=E6=96=B0?= =?UTF-8?q?=E4=BA=BA=E5=88=B8=E5=B7=B2=E5=85=B3=E9=97=AD=E4=BD=86=E6=98=AF?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E6=97=B6=E4=BB=8D=E7=84=B6=E5=8F=91=E6=94=BE?= =?UTF-8?q?=E4=BA=86update=20=20module/promotion/dal/mysql/coupon/CouponTe?= =?UTF-8?q?mplateMapper.java.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 山野羡民 --- .../module/promotion/dal/mysql/coupon/CouponTemplateMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java index 29b7711265..3096a49f3c 100755 --- a/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java +++ b/yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java @@ -47,7 +47,7 @@ public interface CouponTemplateMapper extends BaseMapperX { } default List selectListByTakeType(Integer takeType) { - return selectList(CouponTemplateDO::getTakeType, takeType); + return selectList(CouponTemplateDO::getTakeType, takeType, CouponTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()); } default List selectList(List canTakeTypes, Integer productScope, Long productScopeValue, Integer count) { From c8485a1d6dbf288874f2c2f4ad26cdf6d11bd068 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 20 Feb 2025 12:49:06 +0800 Subject: [PATCH 182/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E7=A7=BB=E9=99=A4=20tongyi=20?= =?UTF-8?q?=E5=BA=93=EF=BC=8C=E5=87=86=E5=A4=87=E6=8D=A2=E5=88=B0=20alibab?= =?UTF-8?q?a=20ai=20=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/tongyi/TongYiAutoConfiguration.java | 253 --------- .../ai/tongyi/TongYiConnectionProperties.java | 52 -- .../ai/tongyi/audio/AudioSpeechModels.java | 40 -- .../audio/AudioTranscriptionModels.java | 43 -- .../audio/speech/TongYiAudioSpeechModel.java | 228 --------- .../speech/TongYiAudioSpeechOptions.java | 261 ---------- .../speech/TongYiAudioSpeechProperties.java | 77 --- .../ai/tongyi/audio/speech/api/Speech.java | 87 ---- .../audio/speech/api/SpeechMessage.java | 80 --- .../audio/speech/api/SpeechMetadata.java | 43 -- .../tongyi/audio/speech/api/SpeechModel.java | 51 -- .../tongyi/audio/speech/api/SpeechPrompt.java | 89 ---- .../audio/speech/api/SpeechResponse.java | 100 ---- .../audio/speech/api/SpeechStreamModel.java | 54 -- .../TongYiAudioTranscriptionModel.java | 187 ------- .../TongYiAudioTranscriptionOptions.java | 203 -------- .../TongYiAudioTranscriptionProperties.java | 72 --- .../api/AudioTranscriptionPrompt.java | 56 -- .../api/AudioTranscriptionResponse.java | 67 --- .../api/AudioTranscriptionResult.java | 68 --- .../cloud/ai/tongyi/chat/TongYiChatModel.java | 483 ------------------ .../ai/tongyi/chat/TongYiChatOptions.java | 463 ----------------- .../ai/tongyi/chat/TongYiChatProperties.java | 83 --- .../common/constants/TongYiConstants.java | 44 -- .../common/exception/TongYiException.java | 38 -- .../exception/TongYiImagesException.java | 39 -- .../embedding/TongYiEmbeddingOptions.java | 84 --- .../embedding/TongYiTextEmbeddingModel.java | 176 ------- .../TongYiTextEmbeddingProperties.java | 50 -- .../ai/tongyi/image/TongYiImagesModel.java | 237 --------- .../ai/tongyi/image/TongYiImagesOptions.java | 187 ------- .../tongyi/image/TongYiImagesProperties.java | 77 --- .../TongYiAiChatResponseMetadata.java | 89 ---- .../ai/tongyi/metadata/TongYiAiUsage.java | 81 --- .../TongYiImagesResponseMetadata.java | 131 ----- .../TongYiTextEmbeddingResponseMetadata.java | 53 -- .../TongYiAudioSpeechResponseMetadata.java | 133 ----- .../TongYiAudioTranscriptionMetadata.java | 43 -- ...gYiAudioTranscriptionResponseMetadata.java | 98 ---- 39 files changed, 4700 deletions(-) delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java deleted file mode 100644 index 1add717c36..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiAutoConfiguration.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi; - -import com.alibaba.cloud.ai.tongyi.audio.speech.TongYiAudioSpeechModel; -import com.alibaba.cloud.ai.tongyi.audio.speech.TongYiAudioSpeechProperties; -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionModel; -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionProperties; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatProperties; -import com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants; -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.cloud.ai.tongyi.embedding.TongYiTextEmbeddingModel; -import com.alibaba.cloud.ai.tongyi.embedding.TongYiTextEmbeddingProperties; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.audio.asr.transcription.Transcription; -import com.alibaba.dashscope.audio.tts.SpeechSynthesizer; -import com.alibaba.dashscope.common.MessageManager; -import com.alibaba.dashscope.embeddings.TextEmbedding; -import com.alibaba.dashscope.exception.NoApiKeyException; -import com.alibaba.dashscope.utils.ApiKey; -import com.alibaba.dashscope.utils.Constants; -import org.springframework.ai.model.function.FunctionCallbackContext; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Scope; - -import java.util.Objects; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@AutoConfiguration -@ConditionalOnClass({ - MessageManager.class, - TongYiChatModel.class, - TongYiImagesModel.class, - TongYiAudioSpeechModel.class, - TongYiTextEmbeddingModel.class, - TongYiAudioTranscriptionModel.class -}) -@EnableConfigurationProperties({ - TongYiChatProperties.class, - TongYiImagesProperties.class, - TongYiAudioSpeechProperties.class, - TongYiConnectionProperties.class, - TongYiTextEmbeddingProperties.class, - TongYiAudioTranscriptionProperties.class -}) -public class TongYiAutoConfiguration { - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public Generation generation() { - - return new Generation(); - } - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public MessageManager msgManager() { - - return new MessageManager(10); - } - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public ImageSynthesis imageSynthesis() { - - return new ImageSynthesis(); - } - - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - public SpeechSynthesizer speechSynthesizer() { - - return new SpeechSynthesizer(); - } - - @Bean - @ConditionalOnMissingBean - public Transcription transcription() { - - return new Transcription(); - } - - @Bean - @ConditionalOnMissingBean - public TextEmbedding textEmbedding() { - - return new TextEmbedding(); - } - - @Bean - @ConditionalOnMissingBean - public FunctionCallbackContext springAiFunctionManager(ApplicationContext context) { - - FunctionCallbackContext manager = new FunctionCallbackContext(); - manager.setApplicationContext(context); - - return manager; - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiChatProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiChatModel tongYiChatClient(Generation generation, - TongYiChatProperties chatOptions, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - - return new TongYiChatModel(generation, chatOptions.getOptions()); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiImagesProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiImagesModel tongYiImagesClient( - ImageSynthesis imageSynthesis, - TongYiImagesProperties imagesOptions, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - - return new TongYiImagesModel(imageSynthesis, imagesOptions.getOptions()); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiAudioSpeechProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiAudioSpeechModel tongYiAudioSpeechClient( - SpeechSynthesizer speechSynthesizer, - TongYiAudioSpeechProperties speechProperties, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - - return new TongYiAudioSpeechModel(speechSynthesizer, speechProperties.getOptions()); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiAudioTranscriptionProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiAudioTranscriptionModel tongYiAudioTranscriptionClient( - Transcription transcription, - TongYiAudioTranscriptionProperties transcriptionProperties, - TongYiConnectionProperties connectionProperties) { - - settingApiKey(connectionProperties); - - return new TongYiAudioTranscriptionModel( - transcriptionProperties.getOptions(), - transcription - ); - } - - @Bean - @ConditionalOnProperty( - prefix = TongYiTextEmbeddingProperties.CONFIG_PREFIX, - name = "enabled", - havingValue = "true", - matchIfMissing = true - ) - public TongYiTextEmbeddingModel tongYiTextEmbeddingClient( - TextEmbedding textEmbedding, - TongYiConnectionProperties connectionProperties - ) { - - settingApiKey(connectionProperties); - return new TongYiTextEmbeddingModel(textEmbedding); - } - - /** - * Setting the API key. - * @param connectionProperties {@link TongYiConnectionProperties} - */ - private void settingApiKey(TongYiConnectionProperties connectionProperties) { - - String apiKey; - - try { - // It is recommended to set the key by defining the api-key in an environment variable. - var envKey = System.getenv(TongYiConstants.SCA_AI_TONGYI_API_KEY); - if (Objects.nonNull(envKey)) { - Constants.apiKey = envKey; - return; - } - if (Objects.nonNull(connectionProperties.getApiKey())) { - apiKey = connectionProperties.getApiKey(); - } - else { - apiKey = ApiKey.getApiKey(null); - } - - Constants.apiKey = apiKey; - } - catch (NoApiKeyException e) { - - throw new TongYiException(e.getMessage()); - } - - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java deleted file mode 100644 index 74141bd227..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/TongYiConnectionProperties.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * Spring Cloud Alibaba AI TongYi LLM connection properties. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiConnectionProperties.CONFIG_PREFIX) -public class TongYiConnectionProperties { - - /** - * Spring Cloud Alibaba AI connection configuration Prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "tongyi"; - - /** - * TongYi LLM API key. - */ - private String apiKey; - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java deleted file mode 100644 index 2c8bd707bf..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioSpeechModels.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio; - -/** - * More models see: https://help.aliyun.com/zh/dashscope/model-list?spm=a2c4g.11186623.0.i5 - * Support all models in list. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public final class AudioSpeechModels { - - private AudioSpeechModels() { - } - - /** - * Male Voice of the Tongue(舌尖男声). - * zh & en. - * Default sample rate: 48 Hz. - */ - public static final String SAMBERT_ZHICHU_V1 = "sambert-zhichu-v1"; - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java deleted file mode 100644 index 4b8435629a..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/AudioTranscriptionModels.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public final class AudioTranscriptionModels { - - private AudioTranscriptionModels() { - } - - /** - * Paraformer Chinese and English speech recognition model supports audio or video speech recognition with a sampling rate of 16kHz or above. - */ - public static final String Paraformer_V1 = "paraformer-v1"; - /** - * Paraformer Chinese speech recognition model, support 8kHz telephone speech recognition. - */ - public static final String Paraformer_8K_V1 = "paraformer-8k-v1"; - /** - * The Paraformer multilingual speech recognition model supports audio or video speech recognition with a sample rate of 16kHz or above. - */ - public static final String Paraformer_MTL_V1 = "paraformer-mtl-v1"; - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java deleted file mode 100644 index f450042da0..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechModel.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.cloud.ai.tongyi.audio.speech.api.*; -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioSpeechResponseMetadata; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisParam; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult; -import com.alibaba.dashscope.audio.tts.SpeechSynthesizer; -import com.alibaba.dashscope.common.ResultCallback; -import io.reactivex.Flowable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.Assert; -import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; - -import java.nio.ByteBuffer; - -/** - * TongYiAudioSpeechClient is a client for TongYi audio speech service for Spring Cloud Alibaba AI. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioSpeechModel implements SpeechModel, SpeechStreamModel { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - /** - * Default speed rate. - */ - private static final float SPEED_RATE = 1.0f; - - /** - * TongYi models api. - */ - private final SpeechSynthesizer speechSynthesizer; - - /** - * TongYi models options. - */ - private final TongYiAudioSpeechOptions defaultOptions; - - /** - * TongYiAudioSpeechClient constructor. - * @param speechSynthesizer the speech synthesizer - */ - public TongYiAudioSpeechModel(SpeechSynthesizer speechSynthesizer) { - - this(speechSynthesizer, null); - } - - /** - * TongYiAudioSpeechClient constructor. - * @param speechSynthesizer the speech synthesizer - * @param tongYiAudioOptions the tongYi audio options - */ - public TongYiAudioSpeechModel(SpeechSynthesizer speechSynthesizer, TongYiAudioSpeechOptions tongYiAudioOptions) { - - Assert.notNull(speechSynthesizer, "speechSynthesizer must not be null"); - Assert.notNull(tongYiAudioOptions, "tongYiAudioOptions must not be null"); - - this.speechSynthesizer = speechSynthesizer; - this.defaultOptions = tongYiAudioOptions; - } - - /** - * Call the TongYi audio speech service. - * @param text the text message to be converted to audio. - * @return the audio byte buffer. - */ - @Override - public ByteBuffer call(String text) { - - var speechRequest = new SpeechPrompt(text); - - return call(speechRequest).getResult().getOutput(); - } - - - /** - * Call the TongYi audio speech service. - * @param prompt the speech prompt. - * @return the speech response. - */ - @Override - public SpeechResponse call(SpeechPrompt prompt) { - - var SCASpeechParam = merge(prompt.getOptions()); - var speechSynthesisParams = toSpeechSynthesisParams(SCASpeechParam); - speechSynthesisParams.setText(prompt.getInstructions().getText()); - logger.info(speechSynthesisParams.toString()); - - var res = speechSynthesizer.call(speechSynthesisParams); - - return convert(res, null); - } - - /** - * Call the TongYi audio speech service. - * @param prompt the speech prompt. - * @param callback the result callback. - * {@link SpeechSynthesizer#call(SpeechSynthesisParam, ResultCallback)} - */ - public void call(SpeechPrompt prompt, ResultCallback callback) { - - var SCASpeechParam = merge(prompt.getOptions()); - var speechSynthesisParams = toSpeechSynthesisParams(SCASpeechParam); - speechSynthesisParams.setText(prompt.getInstructions().getText()); - - speechSynthesizer.call(speechSynthesisParams, callback); - } - - /** - * Stream the TongYi audio speech service. - * @param prompt the speech prompt. - * @return the speech response. - * {@link SpeechSynthesizer#streamCall(SpeechSynthesisParam)} - */ - @Override - public Flux stream(SpeechPrompt prompt) { - - var SCASpeechParam = merge(prompt.getOptions()); - - Flowable resultFlowable = speechSynthesizer - .streamCall(toSpeechSynthesisParams(SCASpeechParam)); - - return Flux.from(resultFlowable) - .flatMap( - res -> Flux.just(res.getAudioFrame()) - .map(audio -> { - var speech = new Speech(audio); - var respMetadata = TongYiAudioSpeechResponseMetadata.from(res); - return new SpeechResponse(speech, respMetadata); - }) - ).publishOn(Schedulers.parallel()); - } - - public TongYiAudioSpeechOptions merge(TongYiAudioSpeechOptions target) { - - var mergeBuilder = TongYiAudioSpeechOptions.builder(); - - mergeBuilder.withModel(defaultOptions.getModel() != null ? defaultOptions.getModel() : target.getModel()); - mergeBuilder.withPitch(defaultOptions.getPitch() != null ? defaultOptions.getPitch() : target.getPitch()); - mergeBuilder.withRate(defaultOptions.getRate() != null ? defaultOptions.getRate() : target.getRate()); - mergeBuilder.withFormat(defaultOptions.getFormat() != null ? defaultOptions.getFormat() : target.getFormat()); - mergeBuilder.withSampleRate(defaultOptions.getSampleRate() != null ? defaultOptions.getSampleRate() : target.getSampleRate()); - mergeBuilder.withTextType(defaultOptions.getTextType() != null ? defaultOptions.getTextType() : target.getTextType()); - mergeBuilder.withVolume(defaultOptions.getVolume() != null ? defaultOptions.getVolume() : target.getVolume()); - mergeBuilder.withEnablePhonemeTimestamp(defaultOptions.isEnablePhonemeTimestamp() != null ? defaultOptions.isEnablePhonemeTimestamp() : target.isEnablePhonemeTimestamp()); - mergeBuilder.withEnableWordTimestamp(defaultOptions.isEnableWordTimestamp() != null ? defaultOptions.isEnableWordTimestamp() : target.isEnableWordTimestamp()); - - return mergeBuilder.build(); - } - - public SpeechSynthesisParam toSpeechSynthesisParams(TongYiAudioSpeechOptions source) { - - var mergeBuilder = SpeechSynthesisParam.builder(); - - mergeBuilder.model(source.getModel() != null ? source.getModel() : AudioSpeechModels.SAMBERT_ZHICHU_V1); - mergeBuilder.text(source.getText() != null ? source.getText() : ""); - - if (source.getFormat() != null) { - mergeBuilder.format(source.getFormat()); - } - if (source.getRate() != null) { - mergeBuilder.rate(source.getRate()); - } - if (source.getPitch() != null) { - mergeBuilder.pitch(source.getPitch()); - } - if (source.getTextType() != null) { - mergeBuilder.textType(source.getTextType()); - } - if (source.getSampleRate() != null) { - mergeBuilder.sampleRate(source.getSampleRate()); - } - if (source.isEnablePhonemeTimestamp() != null) { - mergeBuilder.enablePhonemeTimestamp(source.isEnablePhonemeTimestamp()); - } - if (source.isEnableWordTimestamp() != null) { - mergeBuilder.enableWordTimestamp(source.isEnableWordTimestamp()); - } - if (source.getVolume() != null) { - mergeBuilder.volume(source.getVolume()); - } - - return mergeBuilder.build(); - } - - /** - * Convert the TongYi audio speech service result to the speech response. - * @param result the audio byte buffer. - * @param synthesisResult the synthesis result. - * @return the speech response. - */ - private SpeechResponse convert(ByteBuffer result, SpeechSynthesisResult synthesisResult) { - - if (synthesisResult == null) { - - return new SpeechResponse(new Speech(result)); - } - - var responseMetadata = TongYiAudioSpeechResponseMetadata.from(synthesisResult); - var speech = new Speech(synthesisResult.getAudioFrame()); - - return new SpeechResponse(speech, responseMetadata); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java deleted file mode 100644 index 09f188f8ea..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechOptions.java +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisTextType; -import org.springframework.ai.model.ModelOptions; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioSpeechOptions implements ModelOptions { - - - /** - * Audio Speech models. - */ - private String model = AudioSpeechModels.SAMBERT_ZHICHU_V1; - - /** - * Text content. - */ - private String text; - - /** - * Input text type. - */ - private SpeechSynthesisTextType textType = SpeechSynthesisTextType.PLAIN_TEXT; - - /** - * synthesis audio format. - */ - private SpeechSynthesisAudioFormat format = SpeechSynthesisAudioFormat.WAV; - - /** - * synthesis audio sample rate. - */ - private Integer sampleRate = 16000; - - /** - * synthesis audio volume. - */ - private Integer volume = 50; - - /** - * synthesis audio speed. - */ - private Float rate = 1.0f; - - /** - * synthesis audio pitch. - */ - private Float pitch = 1.0f; - - /** - * enable word level timestamp. - */ - private Boolean enableWordTimestamp = false; - - /** - * enable phoneme level timestamp. - */ - private Boolean enablePhonemeTimestamp = false; - - public static Builder builder() { - return new Builder(); - } - - public String getModel() { - - return model; - } - - public void setModel(String model) { - - this.model = model; - } - - public String getText() { - - return text; - } - - public void setText(String text) { - - this.text = text; - } - - public SpeechSynthesisTextType getTextType() { - - return textType; - } - - public void setTextType(SpeechSynthesisTextType textType) { - - this.textType = textType; - } - - public SpeechSynthesisAudioFormat getFormat() { - - return format; - } - - public void setFormat(SpeechSynthesisAudioFormat format) { - - this.format = format; - } - - public Integer getSampleRate() { - - return sampleRate; - } - - public void setSampleRate(Integer sampleRate) { - - this.sampleRate = sampleRate; - } - - public Integer getVolume() { - - return volume; - } - - public void setVolume(Integer volume) { - - this.volume = volume; - } - - public Float getRate() { - - return rate; - } - - public void setRate(Float rate) { - - this.rate = rate; - } - - public Float getPitch() { - - return pitch; - } - - public void setPitch(Float pitch) { - - this.pitch = pitch; - } - - public Boolean isEnableWordTimestamp() { - - return enableWordTimestamp; - } - - public void setEnableWordTimestamp(Boolean enableWordTimestamp) { - - this.enableWordTimestamp = enableWordTimestamp; - } - - public Boolean isEnablePhonemeTimestamp() { - - return enablePhonemeTimestamp; - } - - public void setEnablePhonemeTimestamp(Boolean enablePhonemeTimestamp) { - - this.enablePhonemeTimestamp = enablePhonemeTimestamp; - } - - /** - * Build a options instances. - */ - public static class Builder { - - private final TongYiAudioSpeechOptions options = new TongYiAudioSpeechOptions(); - - public Builder withModel(String model) { - - options.model = model; - return this; - } - - public Builder withText(String text) { - - options.text = text; - return this; - } - - public Builder withTextType(SpeechSynthesisTextType textType) { - - options.textType = textType; - return this; - } - - public Builder withFormat(SpeechSynthesisAudioFormat format) { - - options.format = format; - return this; - } - - public Builder withSampleRate(Integer sampleRate) { - - options.sampleRate = sampleRate; - return this; - } - - public Builder withVolume(Integer volume) { - - options.volume = volume; - return this; - } - - public Builder withRate(Float rate) { - - options.rate = rate; - return this; - } - - public Builder withPitch(Float pitch) { - - options.pitch = pitch; - return this; - } - - public Builder withEnableWordTimestamp(Boolean enableWordTimestamp) { - - options.enableWordTimestamp = enableWordTimestamp; - return this; - } - - public Builder withEnablePhonemeTimestamp(Boolean enablePhonemeTimestamp) { - - options.enablePhonemeTimestamp = enablePhonemeTimestamp; - return this; - } - - public TongYiAudioSpeechOptions build() { - - return options; - } - - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java deleted file mode 100644 index a3b4357709..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/TongYiAudioSpeechProperties.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech; - -import com.alibaba.cloud.ai.tongyi.audio.AudioSpeechModels; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisAudioFormat; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * TongYi audio speech configuration properties. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiAudioSpeechProperties.CONFIG_PREFIX) -public class TongYiAudioSpeechProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "audio.speech"; - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_AUDIO_MODEL_NAME = AudioSpeechModels.SAMBERT_ZHICHU_V1; - - /** - * Enable TongYiQWEN ai audio client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiAudioSpeechOptions options = TongYiAudioSpeechOptions.builder() - .withModel(DEFAULT_AUDIO_MODEL_NAME) - .withFormat(SpeechSynthesisAudioFormat.WAV) - .build(); - - public TongYiAudioSpeechOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiAudioSpeechOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java deleted file mode 100644 index adeef4e799..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/Speech.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import org.springframework.ai.model.ModelResult; -import org.springframework.lang.Nullable; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Objects; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class Speech implements ModelResult { - - private final ByteBuffer audio; - - private SpeechMetadata speechMetadata; - - public Speech(ByteBuffer audio) { - this.audio = audio; - } - - @Override - public ByteBuffer getOutput() { - return this.audio; - } - - @Override - public SpeechMetadata getMetadata() { - - return speechMetadata != null ? speechMetadata : SpeechMetadata.NULL; - } - - public Speech withSpeechMetadata(@Nullable SpeechMetadata speechMetadata) { - - this.speechMetadata = speechMetadata; - return this; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - if (!(o instanceof Speech that)) { - - return false; - } - - return Arrays.equals(audio.array(), that.audio.array()) - && Objects.equals(speechMetadata, that.speechMetadata); - } - - @Override - public int hashCode() { - - return Objects.hash(Arrays.hashCode(audio.array()), speechMetadata); - } - - @Override - public String toString() { - - return "Speech{" + "text=" + audio + ", speechMetadata=" + speechMetadata + '}'; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java deleted file mode 100644 index 9748bcf507..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMessage.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import java.util.Objects; - -/** - * The {@link SpeechMessage} class represents a single text message to - * be converted to speech by the TongYi LLM TTS. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class SpeechMessage { - - private String text; - - /** - * Constructs a new {@link SpeechMessage} object with the given text. - * @param text the text to be converted to speech - */ - public SpeechMessage(String text) { - this.text = text; - } - - /** - * Returns the text of this speech message. - * @return the text of this speech message - */ - public String getText() { - return text; - } - - /** - * Sets the text of this speech message. - * @param text the new text for this speech message - */ - public void setText(String text) { - this.text = text; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - - if (!(o instanceof SpeechMessage that)) { - - return false; - } - - return Objects.equals(text, that.text); - } - - @Override - public int hashCode() { - - return Objects.hash(text); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java deleted file mode 100644 index 513a2839c1..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechMetadata.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import org.springframework.ai.model.ResultMetadata; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public interface SpeechMetadata extends ResultMetadata { - - /** - * Null Object. - */ - SpeechMetadata NULL = SpeechMetadata.create(); - - /** - * Factory method used to construct a new {@link SpeechMetadata}. - * @return a new {@link SpeechMetadata} - */ - static SpeechMetadata create() { - return new SpeechMetadata() { - }; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java deleted file mode 100644 index 601dce72cf..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechModel.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import org.springframework.ai.model.Model; - -import java.nio.ByteBuffer; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.0.0-RC1 - */ - -@FunctionalInterface -public interface SpeechModel extends Model { - - /** - * Generates spoken audio from the provided text message. - * @param message the text message to be converted to audio. - * @return the resulting audio bytes. - */ - default ByteBuffer call(String message) { - - SpeechPrompt prompt = new SpeechPrompt(message); - - return call(prompt).getResult().getOutput(); - } - - /** - * Sends a speech request to the TongYi TTS API and returns the resulting speech response. - * @param request the speech prompt containing the input text and other parameters. - * @return the speech response containing the generated audio. - */ - SpeechResponse call(SpeechPrompt request); - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java deleted file mode 100644 index dc89ff31a6..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechPrompt.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import com.alibaba.cloud.ai.tongyi.audio.speech.TongYiAudioSpeechOptions; -import org.springframework.ai.model.ModelRequest; - -import java.util.Objects; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class SpeechPrompt implements ModelRequest { - - private TongYiAudioSpeechOptions speechOptions; - - private final SpeechMessage message; - - public SpeechPrompt(String instructions) { - - this(new SpeechMessage(instructions), TongYiAudioSpeechOptions.builder().build()); - } - - public SpeechPrompt(String instructions, TongYiAudioSpeechOptions speechOptions) { - - this(new SpeechMessage(instructions), speechOptions); - } - - public SpeechPrompt(SpeechMessage speechMessage) { - this(speechMessage, TongYiAudioSpeechOptions.builder().build()); - } - - public SpeechPrompt(SpeechMessage speechMessage, TongYiAudioSpeechOptions speechOptions) { - - this.message = speechMessage; - this.speechOptions = speechOptions; - } - - @Override - public SpeechMessage getInstructions() { - return this.message; - } - - @Override - public TongYiAudioSpeechOptions getOptions() { - - return speechOptions; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - if (!(o instanceof SpeechPrompt that)) { - - return false; - } - - return Objects.equals(speechOptions, that.speechOptions) && Objects.equals(message, that.message); - } - - @Override - public int hashCode() { - - return Objects.hash(speechOptions, message); - } - - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java deleted file mode 100644 index b8d7484cf7..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechResponse.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioSpeechResponseMetadata; -import org.springframework.ai.model.ModelResponse; - -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class SpeechResponse implements ModelResponse { - - private final Speech speech; - - private final TongYiAudioSpeechResponseMetadata speechResponseMetadata; - - /** - * Creates a new instance of SpeechResponse with the given speech result. - * @param speech the speech result to be set in the SpeechResponse - * @see Speech - */ - public SpeechResponse(Speech speech) { - this(speech, TongYiAudioSpeechResponseMetadata.NULL); - } - - /** - * Creates a new instance of SpeechResponse with the given speech result and speech - * response metadata. - * @param speech the speech result to be set in the SpeechResponse - * @param speechResponseMetadata the speech response metadata to be set in the - * SpeechResponse - * @see Speech - * @see TongYiAudioSpeechResponseMetadata - */ - public SpeechResponse(Speech speech, TongYiAudioSpeechResponseMetadata speechResponseMetadata) { - - this.speech = speech; - this.speechResponseMetadata = speechResponseMetadata; - } - - @Override - public Speech getResult() { - return speech; - } - - @Override - public List getResults() { - return Collections.singletonList(speech); - } - - @Override - public TongYiAudioSpeechResponseMetadata getMetadata() { - return speechResponseMetadata; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - - return true; - } - if (!(o instanceof SpeechResponse that)) { - - return false; - } - - return Objects.equals(speech, that.speech) - && Objects.equals(speechResponseMetadata, that.speechResponseMetadata); - } - - @Override - public int hashCode() { - - return Objects.hash(speech, speechResponseMetadata); - } - - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java deleted file mode 100644 index d39c79ab2f..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/speech/api/SpeechStreamModel.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.speech.api; - -import org.springframework.ai.model.StreamingModel; -import reactor.core.publisher.Flux; - -import java.nio.ByteBuffer; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@FunctionalInterface -public interface SpeechStreamModel extends StreamingModel { - - /** - * Generates a stream of audio bytes from the provided text message. - * - * @param message the text message to be converted to audio - * @return a Flux of audio bytes representing the generated speech - */ - default Flux stream(String message) { - - SpeechPrompt prompt = new SpeechPrompt(message); - return stream(prompt).map(SpeechResponse::getResult).map(Speech::getOutput); - } - - /** - * Sends a speech request to the TongYi TTS API and returns a stream of the resulting - * speech responses. - * @param prompt the speech prompt containing the input text and other parameters. - * @return a Flux of speech responses, each containing a portion of the generated audio. - */ - @Override - Flux stream(SpeechPrompt prompt); - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java deleted file mode 100644 index 0f0dca9c02..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionModel.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.transcription; - -import cn.hutool.core.collection.ListUtil; -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionPrompt; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionResponse; -import com.alibaba.cloud.ai.tongyi.audio.transcription.api.AudioTranscriptionResult; -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioTranscriptionResponseMetadata; -import com.alibaba.dashscope.audio.asr.transcription.*; -import org.springframework.ai.model.Model; -import org.springframework.core.io.Resource; -import org.springframework.util.Assert; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * TongYiAudioTranscriptionModel is a client for TongYi audio transcription service for - * Spring Cloud Alibaba AI. - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioTranscriptionModel - implements Model { - - /** - * TongYi models options. - */ - private final TongYiAudioTranscriptionOptions defaultOptions; - - /** - * TongYi models api. - */ - private final Transcription transcription; - - public TongYiAudioTranscriptionModel(Transcription transcription) { - this(null, transcription); - } - - public TongYiAudioTranscriptionModel(TongYiAudioTranscriptionOptions defaultOptions, - Transcription transcription) { - Assert.notNull(transcription, "transcription must not be null"); - Assert.notNull(defaultOptions, "defaultOptions must not be null"); - - this.defaultOptions = defaultOptions; - this.transcription = transcription; - } - - @Override - public AudioTranscriptionResponse call(AudioTranscriptionPrompt prompt) { - - TranscriptionParam transcriptionParam; - - if (prompt.getOptions() != null) { - var param = merge(prompt.getOptions()); - transcriptionParam = toTranscriptionParam(param); - transcriptionParam.setFileUrls(prompt.getOptions().getFileUrls()); - } - else { - Resource instructions = prompt.getInstructions(); - try { - transcriptionParam = TranscriptionParam.builder() - .model(AudioTranscriptionModels.Paraformer_V1) - .fileUrls(ListUtil.of(String.valueOf(instructions.getURL()))) - .build(); - } - catch (IOException e) { - throw new TongYiException("Failed to create resource", e); - } - } - - List taskResultList; - try { - // Submit a transcription request - TranscriptionResult result = transcription.asyncCall(transcriptionParam); - // Wait for the transcription to complete - result = transcription.wait(TranscriptionQueryParam - .FromTranscriptionParam(transcriptionParam, result.getTaskId())); - // Get the transcription results - System.out.println(result.getOutput().getAsJsonObject()); - taskResultList = result.getResults(); - System.out.println(Arrays.toString(taskResultList.toArray())); - - return new AudioTranscriptionResponse( - taskResultList.stream().map(taskResult -> - new AudioTranscriptionResult(taskResult.getTranscriptionUrl()) - ).collect(Collectors.toList()), - TongYiAudioTranscriptionResponseMetadata.from(result) - ); - } - catch (Exception e) { - throw new TongYiException("Failed to call audio transcription", e); - } - - } - - public TongYiAudioTranscriptionOptions merge(TongYiAudioTranscriptionOptions target) { - var mergeBuilder = TongYiAudioTranscriptionOptions.builder(); - - mergeBuilder - .withModel(defaultOptions.getModel() != null ? defaultOptions.getModel() - : target.getModel()); - mergeBuilder.withChannelId( - defaultOptions.getChannelId() != null ? defaultOptions.getChannelId() - : target.getChannelId()); - mergeBuilder.withDiarizationEnabled(defaultOptions.getDiarizationEnabled() != null - ? defaultOptions.getDiarizationEnabled() - : target.getDiarizationEnabled()); - mergeBuilder.withDisfluencyRemovalEnabled( - defaultOptions.getDisfluencyRemovalEnabled() != null - ? defaultOptions.getDisfluencyRemovalEnabled() - : target.getDisfluencyRemovalEnabled()); - mergeBuilder.withTimestampAlignmentEnabled( - defaultOptions.getTimestampAlignmentEnabled() != null - ? defaultOptions.getTimestampAlignmentEnabled() - : target.getTimestampAlignmentEnabled()); - mergeBuilder.withSpecialWordFilter(defaultOptions.getSpecialWordFilter() != null - ? defaultOptions.getSpecialWordFilter() - : target.getSpecialWordFilter()); - mergeBuilder.withAudioEventDetectionEnabled( - defaultOptions.getAudioEventDetectionEnabled() != null - ? defaultOptions.getAudioEventDetectionEnabled() - : target.getAudioEventDetectionEnabled()); - - return mergeBuilder.build(); - } - - public TranscriptionParam toTranscriptionParam( - TongYiAudioTranscriptionOptions source) { - var mergeBuilder = TranscriptionParam.builder(); - - mergeBuilder.model(source.getModel() != null ? source.getModel() - : AudioTranscriptionModels.Paraformer_V1); - mergeBuilder.fileUrls( - source.getFileUrls() != null ? source.getFileUrls() : new ArrayList<>()); - if (source.getPhraseId() != null) { - mergeBuilder.phraseId(source.getPhraseId()); - } - if (source.getChannelId() != null) { - mergeBuilder.channelId(source.getChannelId()); - } - if (source.getDiarizationEnabled() != null) { - mergeBuilder.diarizationEnabled(source.getDiarizationEnabled()); - } - if (source.getSpeakerCount() != null) { - mergeBuilder.speakerCount(source.getSpeakerCount()); - } - if (source.getDisfluencyRemovalEnabled() != null) { - mergeBuilder.disfluencyRemovalEnabled(source.getDisfluencyRemovalEnabled()); - } - if (source.getTimestampAlignmentEnabled() != null) { - mergeBuilder.timestampAlignmentEnabled(source.getTimestampAlignmentEnabled()); - } - if (source.getSpecialWordFilter() != null) { - mergeBuilder.specialWordFilter(source.getSpecialWordFilter()); - } - if (source.getAudioEventDetectionEnabled() != null) { - mergeBuilder - .audioEventDetectionEnabled(source.getAudioEventDetectionEnabled()); - } - - return mergeBuilder.build(); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java deleted file mode 100644 index 3fec6375ce..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionOptions.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.transcription; - -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; -import org.springframework.ai.model.ModelOptions; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioTranscriptionOptions implements ModelOptions { - - private String model = AudioTranscriptionModels.Paraformer_V1; - - private List fileUrls = new ArrayList<>(); - - private String phraseId = null; - - private List channelId = Collections.singletonList(0); - - private Boolean diarizationEnabled = false; - - private Integer speakerCount = null; - - private Boolean disfluencyRemovalEnabled = false; - - private Boolean timestampAlignmentEnabled = false; - - private String specialWordFilter = ""; - - private Boolean audioEventDetectionEnabled = false; - - public static Builder builder() { - - return new Builder(); - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public List getFileUrls() { - return fileUrls; - } - - public void setFileUrls(List fileUrls) { - this.fileUrls = fileUrls; - } - - public String getPhraseId() { - return phraseId; - } - - public void setPhraseId(String phraseId) { - this.phraseId = phraseId; - } - - public List getChannelId() { - return channelId; - } - - public void setChannelId(List channelId) { - this.channelId = channelId; - } - - public Boolean getDiarizationEnabled() { - return diarizationEnabled; - } - - public void setDiarizationEnabled(Boolean diarizationEnabled) { - this.diarizationEnabled = diarizationEnabled; - } - - public Integer getSpeakerCount() { - return speakerCount; - } - - public void setSpeakerCount(Integer speakerCount) { - this.speakerCount = speakerCount; - } - - public Boolean getDisfluencyRemovalEnabled() { - return disfluencyRemovalEnabled; - } - - public void setDisfluencyRemovalEnabled(Boolean disfluencyRemovalEnabled) { - this.disfluencyRemovalEnabled = disfluencyRemovalEnabled; - } - - public Boolean getTimestampAlignmentEnabled() { - return timestampAlignmentEnabled; - } - - public void setTimestampAlignmentEnabled(Boolean timestampAlignmentEnabled) { - this.timestampAlignmentEnabled = timestampAlignmentEnabled; - } - - public String getSpecialWordFilter() { - return specialWordFilter; - } - - public void setSpecialWordFilter(String specialWordFilter) { - this.specialWordFilter = specialWordFilter; - } - - public Boolean getAudioEventDetectionEnabled() { - return audioEventDetectionEnabled; - } - - public void setAudioEventDetectionEnabled(Boolean audioEventDetectionEnabled) { - this.audioEventDetectionEnabled = audioEventDetectionEnabled; - } - - /** - * Builder class for constructing TongYiAudioTranscriptionOptions instances. - */ - public static class Builder { - - private final TongYiAudioTranscriptionOptions options = new TongYiAudioTranscriptionOptions(); - - public Builder withModel(String model) { - options.model = model; - return this; - } - - public Builder withFileUrls(List fileUrls) { - options.fileUrls = fileUrls; - return this; - } - - public Builder withPhraseId(String phraseId) { - options.phraseId = phraseId; - return this; - } - - public Builder withChannelId(List channelId) { - options.channelId = channelId; - return this; - } - - public Builder withDiarizationEnabled(Boolean diarizationEnabled) { - options.diarizationEnabled = diarizationEnabled; - return this; - } - - public Builder withSpeakerCount(Integer speakerCount) { - options.speakerCount = speakerCount; - return this; - } - - public Builder withDisfluencyRemovalEnabled(Boolean disfluencyRemovalEnabled) { - options.disfluencyRemovalEnabled = disfluencyRemovalEnabled; - return this; - } - - public Builder withTimestampAlignmentEnabled(Boolean timestampAlignmentEnabled) { - options.timestampAlignmentEnabled = timestampAlignmentEnabled; - return this; - } - - public Builder withSpecialWordFilter(String specialWordFilter) { - options.specialWordFilter = specialWordFilter; - return this; - } - - public Builder withAudioEventDetectionEnabled( - Boolean audioEventDetectionEnabled) { - options.audioEventDetectionEnabled = audioEventDetectionEnabled; - return this; - } - - public TongYiAudioTranscriptionOptions build() { - // Perform any necessary validation here before returning the built object - return options; - } - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java deleted file mode 100644 index fe86d0d72b..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/TongYiAudioTranscriptionProperties.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.transcription; - -import com.alibaba.cloud.ai.tongyi.audio.AudioTranscriptionModels; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiAudioTranscriptionProperties.CONFIG_PREFIX) -public class TongYiAudioTranscriptionProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "audio.transcription"; - - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_AUDIO_MODEL_NAME = AudioTranscriptionModels.Paraformer_V1; - - /** - * Enable TongYiQWEN ai audio client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiAudioTranscriptionOptions options = TongYiAudioTranscriptionOptions - .builder().withModel(DEFAULT_AUDIO_MODEL_NAME).build(); - - public TongYiAudioTranscriptionOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiAudioTranscriptionOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java deleted file mode 100644 index f4c2882456..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionPrompt.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.transcription.api; - -import com.alibaba.cloud.ai.tongyi.audio.transcription.TongYiAudioTranscriptionOptions; -import org.springframework.ai.model.ModelRequest; -import org.springframework.core.io.Resource; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class AudioTranscriptionPrompt implements ModelRequest { - - private Resource audioResource; - - private TongYiAudioTranscriptionOptions transcriptionOptions; - - public AudioTranscriptionPrompt(Resource resource) { - this.audioResource = resource; - } - - public AudioTranscriptionPrompt(Resource resource, TongYiAudioTranscriptionOptions transcriptionOptions) { - this.audioResource = resource; - this.transcriptionOptions = transcriptionOptions; - } - - @Override - public Resource getInstructions() { - - return audioResource; - } - - @Override - public TongYiAudioTranscriptionOptions getOptions() { - - return transcriptionOptions; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java deleted file mode 100644 index 2e740158bb..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResponse.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.transcription.api; - -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioTranscriptionResponseMetadata; -import org.springframework.ai.model.ModelResponse; -import org.springframework.ai.model.ResponseMetadata; - -import java.util.List; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public class AudioTranscriptionResponse implements ModelResponse { - - private List resultList; - - private TongYiAudioTranscriptionResponseMetadata transcriptionResponseMetadata; - - public AudioTranscriptionResponse(List result) { - - this(result, TongYiAudioTranscriptionResponseMetadata.NULL); - } - - public AudioTranscriptionResponse(List result, - TongYiAudioTranscriptionResponseMetadata transcriptionResponseMetadata) { - - this.resultList = List.copyOf(result); - this.transcriptionResponseMetadata = transcriptionResponseMetadata; - } - - @Override - public AudioTranscriptionResult getResult() { - - return this.resultList.get(0); - } - - @Override - public List getResults() { - - return this.resultList; - } - - @Override - public ResponseMetadata getMetadata() { - - return this.transcriptionResponseMetadata; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java deleted file mode 100644 index 651855caf2..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/audio/transcription/api/AudioTranscriptionResult.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.audio.transcription.api; - -import com.alibaba.cloud.ai.tongyi.metadata.audio.TongYiAudioTranscriptionMetadata; -import org.springframework.ai.model.ModelResult; -import org.springframework.ai.model.ResultMetadata; - -import java.util.Objects; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ -public class AudioTranscriptionResult implements ModelResult { - - private String text; - - private TongYiAudioTranscriptionMetadata transcriptionMetadata; - - public AudioTranscriptionResult(String text) { - this.text = text; - } - - @Override - public String getOutput() { - - return this.text; - } - - @Override - public ResultMetadata getMetadata() { - - return transcriptionMetadata != null ? transcriptionMetadata : TongYiAudioTranscriptionMetadata.NULL; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - AudioTranscriptionResult that = (AudioTranscriptionResult) o; - return Objects.equals(text, that.text) && Objects.equals(transcriptionMetadata, that.transcriptionMetadata); - } - - @Override - public int hashCode() { - return Objects.hash(text, transcriptionMetadata); - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java deleted file mode 100644 index 11328a02e5..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatModel.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.chat; - -import cn.hutool.core.collection.ListUtil; -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.dashscope.aigc.conversation.ConversationParam; -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.generation.GenerationOutput; -import com.alibaba.dashscope.aigc.generation.GenerationResult; -import com.alibaba.dashscope.common.MessageManager; -import com.alibaba.dashscope.common.Role; -import com.alibaba.dashscope.exception.InputRequiredException; -import com.alibaba.dashscope.exception.NoApiKeyException; -import com.alibaba.dashscope.tools.FunctionDefinition; -import com.alibaba.dashscope.tools.ToolCallBase; -import com.alibaba.dashscope.tools.ToolCallFunction; -import com.alibaba.dashscope.utils.ApiKeywords; -import com.alibaba.dashscope.utils.JsonUtils; -import io.reactivex.Flowable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.metadata.ChatGenerationMetadata; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.model.StreamingChatModel; -import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.ModelOptionsUtils; -import org.springframework.ai.model.function.AbstractFunctionCallSupport; -import org.springframework.ai.model.function.FunctionCallbackContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.CollectionUtils; -import reactor.core.publisher.Flux; -import reactor.core.scheduler.Schedulers; - -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; - - -/** - * {@link ChatModel} and {@link StreamingChatModel} implementation for {@literal Alibaba DashScope} - * backed by {@link Generation}. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - * @see ChatModel - * @see com.alibaba.dashscope.aigc.generation - */ - -public class TongYiChatModel extends - AbstractFunctionCallSupport< - com.alibaba.dashscope.common.Message, - ConversationParam, - GenerationResult> - implements ChatModel, StreamingChatModel { - - private static final Logger logger = LoggerFactory.getLogger(TongYiChatModel.class); - - /** - * DashScope generation client. - */ - private final Generation generation; - - /** - * The TongYi models default chat completion api. - */ - private TongYiChatOptions defaultOptions; - - /** - * User role message manager. - */ - @Autowired - private MessageManager msgManager; - - /** - * Initializes an instance of the TongYiChatClient. - * @param generation DashScope generation client. - */ - public TongYiChatModel(Generation generation) { - - this(generation, - TongYiChatOptions.builder() - .withTopP(0.8) - .withEnableSearch(true) - .withResultFormat(ConversationParam.ResultFormat.MESSAGE) - .build(), - null - ); - } - - /** - * Initializes an instance of the TongYiChatClient. - * @param generation DashScope generation client. - * @param options TongYi model params. - */ - public TongYiChatModel(Generation generation, TongYiChatOptions options) { - - this(generation, options, null); - } - - /** - * Create a TongYi models client. - * @param generation DashScope model generation client. - * @param options TongYi default chat completion api. - */ - public TongYiChatModel(Generation generation, TongYiChatOptions options, - FunctionCallbackContext functionCallbackContext) { - - super(functionCallbackContext); - this.generation = generation; - this.defaultOptions = options; - } - - /** - * Get default sca chat options. - * - * @return TongYiChatOptions default object. - */ - public TongYiChatOptions getDefaultOptions() { - - return this.defaultOptions; - } - - @Override - public ChatResponse call(Prompt prompt) { - - ConversationParam params = toTongYiChatParams(prompt); - - // TongYi models context loader. - com.alibaba.dashscope.common.Message message = new com.alibaba.dashscope.common.Message(); - message.setRole(Role.USER.getValue()); - message.setContent(prompt.getContents()); - msgManager.add(message); - params.setMessages(msgManager.get()); - - logger.trace("TongYi ConversationOptions: {}", params); - GenerationResult chatCompletions = this.callWithFunctionSupport(params); - logger.trace("TongYi ConversationOptions: {}", params); - - msgManager.add(chatCompletions); - - List generations = - chatCompletions - .getOutput() - .getChoices() - .stream() - .map(choice -> - new org.springframework.ai.chat.model.Generation( - choice - .getMessage() - .getContent() - ).withGenerationMetadata(generateChoiceMetadata(choice) - )) - .toList(); - - return new ChatResponse(generations); - - } - - @Override - public Flux stream(Prompt prompt) { - - Flowable genRes; - ConversationParam tongYiChatParams = toTongYiChatParams(prompt); - - // See https://help.aliyun.com/zh/dashscope/developer-reference/api-details?spm=a2c4g.11186623.0.0.655fc11aRR0jj7#b9ad0a10cfhpe - // tongYiChatParams.setIncrementalOutput(true); - - try { - genRes = generation.streamCall(tongYiChatParams); - } - catch (NoApiKeyException | InputRequiredException e) { - logger.warn("TongYi chat client: " + e.getMessage()); - throw new TongYiException(e.getMessage()); - } - - return Flux.from(genRes) - .flatMap( - message -> Flux.just( - message.getOutput() - .getChoices() - .get(0) - .getMessage() - .getContent()) - .map(content -> { - var gen = new org.springframework.ai.chat.model.Generation(content) - .withGenerationMetadata(generateChoiceMetadata( - message.getOutput() - .getChoices() - .get(0) - )); - return new ChatResponse(ListUtil.of(gen)); - }) - ) - .publishOn(Schedulers.parallel()); - - } - - /** - * Configuration properties to Qwen model params. - * Test access. - * - * @param prompt {@link Prompt} - * @return Qwen models params {@link ConversationParam} - */ - public ConversationParam toTongYiChatParams(Prompt prompt) { - - Set functionsForThisRequest = new HashSet<>(); - - List tongYiMessage = prompt.getInstructions().stream() - .map(this::fromSpringAIMessage) - .toList(); - - ConversationParam chatParams = ConversationParam.builder() - .messages(tongYiMessage) - // models setting - // {@link HalfDuplexServiceParam#models} - .model(Generation.Models.QWEN_TURBO) - // {@link GenerationOutput} - .resultFormat(ConversationParam.ResultFormat.MESSAGE) - .incrementalOutput(true) - - .build(); - - if (this.defaultOptions != null) { - - chatParams = merge(chatParams, this.defaultOptions); - Set defaultEnabledFunctions = this.handleFunctionCallbackConfigurations(this.defaultOptions, !IS_RUNTIME_CALL); - functionsForThisRequest.addAll(defaultEnabledFunctions); - } - if (prompt.getOptions() != null) { - if (prompt.getOptions() instanceof ChatOptions runtimeOptions) { - TongYiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions, - ChatOptions.class, TongYiChatOptions.class); - - chatParams = merge(updatedRuntimeOptions, chatParams); - - Set promptEnabledFunctions = this.handleFunctionCallbackConfigurations(updatedRuntimeOptions, - IS_RUNTIME_CALL); - functionsForThisRequest.addAll(promptEnabledFunctions); - - } - else { - throw new IllegalArgumentException("Prompt options are not of type ConversationParam:" - + prompt.getOptions().getClass().getSimpleName()); - } - } - - // Add the enabled functions definitions to the request's tools parameter. - - if (!CollectionUtils.isEmpty(functionsForThisRequest)) { - List tools = this.getFunctionTools(functionsForThisRequest); - - // todo chatParams.setTools(tools) - } - - return chatParams; - } - - private ChatGenerationMetadata generateChoiceMetadata(GenerationOutput.Choice choice) { - - return ChatGenerationMetadata.from( - String.valueOf(choice.getFinishReason()), - choice.getMessage().getContent() - ); - } - - private List getFunctionTools(Set functionNames) { - return this.resolveFunctionCallbacks(functionNames).stream().map(functionCallback -> { - - FunctionDefinition functionDefinition = FunctionDefinition.builder() - .name(functionCallback.getName()) - .description(functionCallback.getDescription()) - .parameters(JsonUtils.parametersToJsonObject( - ModelOptionsUtils.jsonToMap(functionCallback.getInputTypeSchema()) - )) - .build(); - - return functionDefinition; - }).toList(); - } - - - private ConversationParam merge(ConversationParam tongYiParams, TongYiChatOptions scaChatParams) { - - if (scaChatParams == null) { - - return tongYiParams; - } - - return ConversationParam.builder() - .messages(tongYiParams.getMessages()) - .maxTokens((tongYiParams.getMaxTokens() != null) ? tongYiParams.getMaxTokens() : scaChatParams.getMaxTokens()) - // When merge options. Because ConversationParams is must not null. So is setting. - .model(scaChatParams.getModel()) - .resultFormat((tongYiParams.getResultFormat() != null) ? tongYiParams.getResultFormat() : scaChatParams.getResultFormat()) - .enableSearch((tongYiParams.getEnableSearch() != null) ? tongYiParams.getEnableSearch() : scaChatParams.getEnableSearch()) - .topK((tongYiParams.getTopK() != null) ? tongYiParams.getTopK() : scaChatParams.getTopK()) - .topP((tongYiParams.getTopP() != null) ? tongYiParams.getTopP() : scaChatParams.getTopP()) - .incrementalOutput((tongYiParams.getIncrementalOutput() != null) ? tongYiParams.getIncrementalOutput() : scaChatParams.getIncrementalOutput()) - .temperature((tongYiParams.getTemperature() != null) ? tongYiParams.getTemperature() : scaChatParams.getTemperature()) - .repetitionPenalty((tongYiParams.getRepetitionPenalty() != null) ? tongYiParams.getRepetitionPenalty() : scaChatParams.getRepetitionPenalty()) - .seed((tongYiParams.getSeed() != null) ? tongYiParams.getSeed() : scaChatParams.getSeed()) - .build(); - - } - - private ConversationParam merge(TongYiChatOptions scaChatParams, ConversationParam tongYiParams) { - - if (scaChatParams == null) { - - return tongYiParams; - } - - ConversationParam mergedTongYiParams = ConversationParam.builder() - .model(Generation.Models.QWEN_TURBO) - .messages(tongYiParams.getMessages()) - .build(); - mergedTongYiParams = merge(tongYiParams, scaChatParams); - - if (scaChatParams.getMaxTokens() != null) { - mergedTongYiParams.setMaxTokens(scaChatParams.getMaxTokens()); - } - - if (scaChatParams.getStop() != null) { - mergedTongYiParams.setStopStrings(scaChatParams.getStop()); - } - - if (scaChatParams.getTemperature() != null) { - mergedTongYiParams.setTemperature(scaChatParams.getTemperature()); - } - - if (scaChatParams.getTopK() != null) { - mergedTongYiParams.setTopK(scaChatParams.getTopK()); - } - - if (scaChatParams.getTopK() != null) { - mergedTongYiParams.setTopK(scaChatParams.getTopK()); - } - - return mergedTongYiParams; - } - - private com.alibaba.dashscope.common.Message fromSpringAIMessage(Message message) { - - return switch (message.getMessageType()) { - case USER -> com.alibaba.dashscope.common.Message.builder() - .role(Role.USER.getValue()) - .content(message.getContent()) - .build(); - case SYSTEM -> com.alibaba.dashscope.common.Message.builder() - .role(Role.SYSTEM.getValue()) - .content(message.getContent()) - .build(); - case ASSISTANT -> com.alibaba.dashscope.common.Message.builder() - .role(Role.ASSISTANT.getValue()) - .content(message.getContent()) - .build(); - default -> throw new IllegalArgumentException("Unknown message type " + message.getMessageType()); - }; - - } - - @Override - protected ConversationParam doCreateToolResponseRequest( - ConversationParam previousRequest, - com.alibaba.dashscope.common.Message responseMessage, - List conversationHistory - ) { - for (ToolCallBase toolCall : responseMessage.getToolCalls()) { - if (toolCall instanceof ToolCallFunction toolCallFunction) { - if (toolCallFunction.getFunction() != null) { - var functionName = toolCallFunction.getFunction().getName(); - var functionArguments = toolCallFunction.getFunction().getArguments(); - - if (!this.functionCallbackRegister.containsKey(functionName)) { - throw new IllegalStateException("No function callback found for function name: " + functionName); - } - - String functionResponse = this.functionCallbackRegister.get(functionName).call(functionArguments); - - // Add the function response to the conversation. - conversationHistory - .add(com.alibaba.dashscope.common.Message.builder() - .content(functionResponse) - .role(Role.BOT.getValue()) - .toolCallId(toolCall.getId()) - .build() - ); - } - } - - } - - ConversationParam newRequest = ConversationParam.builder().messages(conversationHistory).build(); - - // todo: No @JsonProperty fields. - newRequest = ModelOptionsUtils.merge(newRequest, previousRequest, ConversationParam.class); - - return newRequest; - - } - - @Override - protected List doGetUserMessages(ConversationParam request) { - - return request.getMessages(); - } - - @Override - protected com.alibaba.dashscope.common.Message doGetToolResponseMessage(GenerationResult response) { - - var message = response.getOutput().getChoices().get(0).getMessage(); - var assistantMessage = com.alibaba.dashscope.common.Message.builder().role(Role.ASSISTANT.getValue()) - .content("").build(); - assistantMessage.setToolCalls(message.getToolCalls()); - - return assistantMessage; - } - - @Override - protected GenerationResult doChatCompletion(ConversationParam request) { - - GenerationResult result; - try { - result = generation.call(request); - } - catch (NoApiKeyException | InputRequiredException e) { - throw new RuntimeException(e); - } - - return result; - } - - @Override - protected Flux doChatCompletionStream(ConversationParam request) { - final Flowable genRes; - try { - genRes = generation.streamCall(request); - } - catch (NoApiKeyException | InputRequiredException e) { - logger.warn("TongYi chat client: " + e.getMessage()); - throw new TongYiException(e.getMessage()); - } - return Flux.from(genRes); - - } - - @Override - protected boolean isToolFunctionCall(GenerationResult response) { - - if (response == null || CollectionUtils.isEmpty(response.getOutput().getChoices())) { - - return false; - } - var choice = response.getOutput().getChoices().get(0); - if (choice == null || choice.getFinishReason() == null) { - - return false; - } - - return Objects.equals(choice.getFinishReason(), ApiKeywords.TOOL_CALLS); - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java deleted file mode 100644 index 20005fd990..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatOptions.java +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.chat; - -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.generation.GenerationParam; -import org.springframework.ai.chat.prompt.ChatOptions; -import org.springframework.ai.model.function.FunctionCallback; -import org.springframework.ai.model.function.FunctionCallingOptions; -import org.springframework.util.Assert; - -import java.util.*; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiChatOptions implements FunctionCallingOptions, ChatOptions { - - /** - * TongYi Models. - * {@link Generation.Models} - */ - private String model = Generation.Models.QWEN_TURBO; - - /** - * The random number seed used in generation, the user controls the randomness of the content generated by the model. - * seed supports unsigned 64-bit integers, with a default value of 1234. - * when using seed, the model will generate the same or similar results as much as possible, but there is currently no guarantee that the results will be exactly the same each time. - */ - private Integer seed = 1234; - - /** - * Used to specify the maximum number of tokens that the model can generate when generating content, - * it defines the upper limit of generation but does not guarantee that this number will be generated every time. - * For qwen-turbo the maximum and default values are 1500 tokens. - * The qwen-max, qwen-max-1201, qwen-max-longcontext, and qwen-plus models have a maximum and default value of 2000 tokens. - */ - private Integer maxTokens = 1500; - - /** - * The generation process kernel sampling method probability threshold, - * for example, takes the value of 0.8, only retains the smallest set of the most probable tokens with probabilities that add up to greater than or equal to 0.8 as the candidate set. - * The range of values is (0,1.0), the larger the value, the higher the randomness of generation; the lower the value, the higher the certainty of generation. - */ - private Double topP = 0.8; - - /** - * The size of the sampling candidate set at the time of generation. - * For example, with a value of 50, only the 50 highest scoring tokens in a single generation will form a randomly sampled candidate set. - * The larger the value, the higher the randomness of the generation; the smaller the value, the higher the certainty of the generation. - * This parameter is not passed by default, and a value of None or when top_k is greater than 100 indicates that the top_k policy is not enabled, - * at which time, only the top_p policy is in effect. - */ - private Integer topK; - - /** - * Used to control the repeatability of model generation. - * Increasing repetition_penalty reduces the repetition of model generation. 1.0 means no penalty. - */ - private Double repetitionPenalty = 1.1; - - /** - * is used to control the degree of randomness and diversity. - * Specifically, the temperature value controls the extent to which the probability distribution of each candidate word is smoothed when generating text. - * Higher values of temperature reduce the peak of the probability distribution, allowing more low-probability words to be selected and generating more diverse results, - * while lower values of temperature enhance the peak of the probability distribution, making it easier for high-probability words to be selected and generating more certain results. - * Range: [0, 2), 0 is not recommended, meaningless. - * java version >= 2.5.1 - */ - private Double temperature = 0.85; - - /** - * The stop parameter is used to realize precise control of the content generation process, automatically stopping when the generated content is about to contain the specified string or token_ids, - * and the generated content does not contain the specified content. - * For example, if stop is specified as "Hello", it means stop when "Hello" will be generated; if stop is specified as [37763, 367], it means stop when "Observation" will be generated. - * The stop parameter can be passed as a list of arrays of strings or token_ids to support the scenario of using multiple stops. - * Explanation: Do not mix strings and token_ids in list mode, the element types should be the same in list mode. - */ - private List stop; - - /** - * Whether or not to use stream output. When outputting the result in stream mode, the interface returns the result as generator, - * you need to iterate to get the result, the default output is the whole sequence of the current generation for each output, - * the last output is the final result of all the generation, you can change the output mode to non-incremental output by the parameter incremental_output to False. - */ - private Boolean stream = false; - - /** - * The model has a built-in Internet search service. - * This parameter controls whether the model refers to the use of Internet search results when generating text. The values are as follows: - * True: enable internet search, the model will use the search result as the reference information in the text generation process, but the model will "judge by itself" whether to use the internet search result based on its internal logic. - * False (default): Internet search is disabled. - */ - private Boolean enableSearch = false; - - /** - * [text|message], defaults to text, when it is message, - * the output refers to the message result example. - * It is recommended to prioritize the use of message format. - */ - private String resultFormat = GenerationParam.ResultFormat.MESSAGE; - - /** - * Control the streaming output mode, that is, the content will contain the content has been output; - * set to True, will open the incremental output mode, the output will not contain the content has been output, - * you need to splice the whole output, refer to the streaming output sample code. - */ - private Boolean incrementalOutput = false; - - /** - * A list of tools that the model can optionally call. - * Currently only functions are supported, and even if multiple functions are entered, the model will only select one to generate the result. - */ - private List tools; - - @Override - public Float getTemperature() { - - return this.temperature.floatValue(); - } - - public void setTemperature(Float temperature) { - - this.temperature = temperature.doubleValue(); - } - - @Override - public Float getTopP() { - - return this.topP.floatValue(); - } - - public void setTopP(Float topP) { - - this.topP = topP.doubleValue(); - } - - @Override - public Integer getTopK() { - - return this.topK; - } - - public void setTopK(Integer topK) { - - this.topK = topK; - } - - public String getModel() { - - return model; - } - - public void setModel(String model) { - - this.model = model; - } - - public Integer getSeed() { - - return seed; - } - - public String getResultFormat() { - - return resultFormat; - } - - public void setResultFormat(String resultFormat) { - - this.resultFormat = resultFormat; - } - - public void setSeed(Integer seed) { - - this.seed = seed; - } - - public Integer getMaxTokens() { - - return maxTokens; - } - - public void setMaxTokens(Integer maxTokens) { - - this.maxTokens = maxTokens; - } - - public Float getRepetitionPenalty() { - - return repetitionPenalty.floatValue(); - } - - public void setRepetitionPenalty(Float repetitionPenalty) { - - this.repetitionPenalty = repetitionPenalty.doubleValue(); - } - - public List getStop() { - - return stop; - } - - public void setStop(List stop) { - - this.stop = stop; - } - - public Boolean getStream() { - - return stream; - } - - public void setStream(Boolean stream) { - - this.stream = stream; - } - - public Boolean getEnableSearch() { - - return enableSearch; - } - - public void setEnableSearch(Boolean enableSearch) { - - this.enableSearch = enableSearch; - } - - public Boolean getIncrementalOutput() { - - return incrementalOutput; - } - - public void setIncrementalOutput(Boolean incrementalOutput) { - - this.incrementalOutput = incrementalOutput; - } - - public List getTools() { - - return tools; - } - - public void setTools(List tools) { - - this.tools = tools; - } - - private List functionCallbacks = new ArrayList<>(); - - private Set functions = new HashSet<>(); - - @Override - public List getFunctionCallbacks() { - - return this.functionCallbacks; - } - - @Override - public void setFunctionCallbacks(List functionCallbacks) { - - this.functionCallbacks = functionCallbacks; - } - - @Override - public Set getFunctions() { - - return this.functions; - } - - @Override - public void setFunctions(Set functions) { - - this.functions = functions; - } - - @Override - public boolean equals(Object o) { - - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - TongYiChatOptions that = (TongYiChatOptions) o; - - return Objects.equals(model, that.model) - && Objects.equals(seed, that.seed) - && Objects.equals(maxTokens, that.maxTokens) - && Objects.equals(topP, that.topP) - && Objects.equals(topK, that.topK) - && Objects.equals(repetitionPenalty, that.repetitionPenalty) - && Objects.equals(temperature, that.temperature) - && Objects.equals(stop, that.stop) - && Objects.equals(stream, that.stream) - && Objects.equals(enableSearch, that.enableSearch) - && Objects.equals(resultFormat, that.resultFormat) - && Objects.equals(incrementalOutput, that.incrementalOutput) - && Objects.equals(tools, that.tools) - && Objects.equals(functionCallbacks, that.functionCallbacks) - && Objects.equals(functions, that.functions); - } - - @Override - public int hashCode() { - - return Objects.hash( - model, - seed, - maxTokens, - topP, - topK, - repetitionPenalty, - temperature, - stop, - stream, - enableSearch, - resultFormat, - incrementalOutput, - tools, - functionCallbacks, - functions - ); - } - - @Override - public String toString() { - - final StringBuilder sb = new StringBuilder("TongYiChatOptions{"); - - sb.append(", model='").append(model).append('\''); - sb.append(", seed=").append(seed); - sb.append(", maxTokens=").append(maxTokens); - sb.append(", topP=").append(topP); - sb.append(", topK=").append(topK); - sb.append(", repetitionPenalty=").append(repetitionPenalty); - sb.append(", temperature=").append(temperature); - sb.append(", stop=").append(stop); - sb.append(", stream=").append(stream); - sb.append(", enableSearch=").append(enableSearch); - sb.append(", resultFormat='").append(resultFormat).append('\''); - sb.append(", incrementalOutput=").append(incrementalOutput); - sb.append(", tools=").append(tools); - sb.append(", functionCallbacks=").append(functionCallbacks); - sb.append(", functions=").append(functions); - sb.append('}'); - - return sb.toString(); - } - - public static Builder builder() { - - return new Builder(); - } - - public static class Builder { - - protected TongYiChatOptions options; - - public Builder() { - - this.options = new TongYiChatOptions(); - } - - public Builder(TongYiChatOptions options) { - - this.options = options; - } - - public Builder withModel(String model) { - this.options.model = model; - return this; - } - - public Builder withMaxTokens(Integer maxTokens) { - this.options.maxTokens = maxTokens; - return this; - } - - public Builder withResultFormat(String rf) { - this.options.resultFormat = rf; - return this; - } - - public Builder withEnableSearch(Boolean enableSearch) { - this.options.enableSearch = enableSearch; - return this; - } - - public Builder withFunctionCallbacks(List functionCallbacks) { - this.options.functionCallbacks = functionCallbacks; - return this; - } - - public Builder withFunctions(Set functionNames) { - Assert.notNull(functionNames, "Function names must not be null"); - this.options.functions = functionNames; - return this; - } - - public Builder withFunction(String functionName) { - Assert.hasText(functionName, "Function name must not be empty"); - this.options.functions.add(functionName); - return this; - } - - public Builder withSeed(Integer seed) { - this.options.seed = seed; - return this; - } - - public Builder withStop(List stop) { - this.options.stop = stop; - return this; - } - - public Builder withTemperature(Double temperature) { - this.options.temperature = temperature; - return this; - } - - public Builder withTopP(Double topP) { - this.options.topP = topP; - return this; - } - - public Builder withTopK(Integer topK) { - this.options.topK = topK; - return this; - } - - public Builder withRepetitionPenalty(Double repetitionPenalty) { - this.options.repetitionPenalty = repetitionPenalty; - return this; - } - - public TongYiChatOptions build() { - - return this.options; - } - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java deleted file mode 100644 index 9a7e85afb1..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/chat/TongYiChatProperties.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.chat; - -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.generation.GenerationParam; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiChatProperties.CONFIG_PREFIX) -public class TongYiChatProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "chat"; - - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_DEPLOYMENT_NAME = Generation.Models.QWEN_TURBO; - - /** - * Default temperature speed. - */ - private static final Double DEFAULT_TEMPERATURE = 0.8; - - /** - * Enable TongYiQWEN ai chat client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiChatOptions options = TongYiChatOptions.builder() - .withModel(DEFAULT_DEPLOYMENT_NAME) - .withTemperature(DEFAULT_TEMPERATURE) - .withEnableSearch(true) - .withResultFormat(GenerationParam.ResultFormat.MESSAGE) - .build(); - - public TongYiChatOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiChatOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java deleted file mode 100644 index 6fe77bd309..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/constants/TongYiConstants.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024-2025 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.common.constants; - -/** - * @author yuluo - * @author yuluo - */ - -public final class TongYiConstants { - - private TongYiConstants() { - } - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String SCA_AI_CONFIGURATION = "spring.cloud.ai.tongyi."; - - /** - * Spring Cloud Alibaba AI constants prefix. - */ - public static final String SCA_AI = "SPRING_CLOUD_ALIBABA_"; - - /** - * TongYi AI apikey env name. - */ - public static final String SCA_AI_TONGYI_API_KEY = SCA_AI + "TONGYI_API_KEY"; - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java deleted file mode 100644 index 18e14ad764..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.common.exception; - -/** - * TongYi models runtime exception. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiException extends RuntimeException { - - public TongYiException(String message) { - - super(message); - } - - public TongYiException(String message, Throwable cause) { - - super(message, cause); - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java deleted file mode 100644 index 5e2e577e97..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/common/exception/TongYiImagesException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.common.exception; - -/** - * TongYi models images exception. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesException extends TongYiException { - - public TongYiImagesException(String message) { - - super(message); - } - - public TongYiImagesException(String message, Throwable cause) { - - super(message, cause); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java deleted file mode 100644 index 2ad81258f0..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiEmbeddingOptions.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.embedding; - -import com.alibaba.dashscope.embeddings.TextEmbeddingParam; -import org.springframework.ai.embedding.EmbeddingOptions; - -import java.util.List; - -/** - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -public final class TongYiEmbeddingOptions implements EmbeddingOptions { - - private List texts; - - private TextEmbeddingParam.TextType textType; - - public List getTexts() { - return texts; - } - - public void setTexts(List texts) { - this.texts = texts; - } - - public TextEmbeddingParam.TextType getTextType() { - return textType; - } - - public void setTextType(TextEmbeddingParam.TextType textType) { - this.textType = textType; - } - - public static Builder builder() { - return new Builder(); - } - - public final static class Builder { - - private final TongYiEmbeddingOptions options; - - private Builder() { - this.options = new TongYiEmbeddingOptions(); - } - - public Builder withtexts(List texts) { - - options.setTexts(texts); - return this; - } - - public Builder withtextType(TextEmbeddingParam.TextType textType) { - - options.setTextType(textType); - return this; - } - - public TongYiEmbeddingOptions build() { - - return options; - } - - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java deleted file mode 100644 index 99a356fe83..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingModel.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.embedding; - -import cn.hutool.core.collection.ListUtil; -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiException; -import com.alibaba.cloud.ai.tongyi.metadata.TongYiTextEmbeddingResponseMetadata; -import com.alibaba.dashscope.embeddings.TextEmbedding; -import com.alibaba.dashscope.embeddings.TextEmbeddingParam; -import com.alibaba.dashscope.embeddings.TextEmbeddingResult; -import com.alibaba.dashscope.embeddings.TextEmbeddingResultItem; -import com.alibaba.dashscope.exception.InputRequiredException; -import com.alibaba.dashscope.exception.NoApiKeyException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.document.Document; -import org.springframework.ai.document.MetadataMode; -import org.springframework.ai.embedding.AbstractEmbeddingModel; -import org.springframework.ai.embedding.Embedding; -import org.springframework.ai.embedding.EmbeddingRequest; -import org.springframework.ai.embedding.EmbeddingResponse; -import org.springframework.util.Assert; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * {@link TongYiTextEmbeddingModel} implementation for {@literal Alibaba DashScope}. - * - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -public class TongYiTextEmbeddingModel extends AbstractEmbeddingModel { - - private final Logger logger = LoggerFactory.getLogger(TongYiTextEmbeddingModel.class); - - /** - * TongYi Text Embedding client. - */ - private final TextEmbedding textEmbedding; - - /** - * {@link MetadataMode}. - */ - private final MetadataMode metadataMode; - - private final TongYiEmbeddingOptions defaultOptions; - - public TongYiTextEmbeddingModel(TextEmbedding textEmbedding) { - - this(textEmbedding, MetadataMode.EMBED); - } - - public TongYiTextEmbeddingModel(TextEmbedding textEmbedding, MetadataMode metadataMode) { - - this(textEmbedding, metadataMode, - TongYiEmbeddingOptions.builder() - .withtextType(TextEmbeddingParam.TextType.DOCUMENT) - .build() - ); - } - - public TongYiTextEmbeddingModel( - TextEmbedding textEmbedding, - MetadataMode metadataMode, - TongYiEmbeddingOptions options - ) { - Assert.notNull(textEmbedding, "textEmbedding must not be null"); - Assert.notNull(metadataMode, "Metadata mode must not be null"); - Assert.notNull(options, "TongYiEmbeddingOptions must not be null"); - - this.metadataMode = metadataMode; - this.textEmbedding = textEmbedding; - this.defaultOptions = options; - } - - public TongYiEmbeddingOptions getDefaultOptions() { - - return this.defaultOptions; - } - - @Override - public List embed(Document document) { - - return this.call( - new EmbeddingRequest( - ListUtil.of(document.getFormattedContent(this.metadataMode)), - null) - ).getResults().stream() - .map(Embedding::getOutput) - .flatMap(List::stream) - .toList(); - } - - @Override - public EmbeddingResponse call(EmbeddingRequest request) { - - TextEmbeddingParam embeddingParams = toEmbeddingParams(request); - logger.debug("Embedding request: {}", embeddingParams); - TextEmbeddingResult resp; - - try { - resp = textEmbedding.call(embeddingParams); - } - catch (NoApiKeyException e) { - throw new TongYiException(e.getMessage()); - } - - return genEmbeddingResp(resp); - } - - private EmbeddingResponse genEmbeddingResp(TextEmbeddingResult result) { - - return new EmbeddingResponse( - genEmbeddingList(result.getOutput().getEmbeddings()), - TongYiTextEmbeddingResponseMetadata.from(result.getUsage()) - ); - } - - private List genEmbeddingList(List embeddings) { - - return embeddings.stream() - .map(embedding -> - new Embedding( - embedding.getEmbedding(), - embedding.getTextIndex() - )) - .collect(Collectors.toList()); - } - - /** - * We recommend setting the model parameters by passing the embedding parameters through the code; - * yml configuration compatibility is not considered here. - * It is not recommended that users set parameters from yml, - * as this reduces the flexibility of the configuration. - * Because the model name keeps changing, strings are used here and constants are undefined: - * Model list: Text Embedding Model List - * @param requestOptions Client params. {@link EmbeddingRequest} - * @return {@link TextEmbeddingParam} - */ - private TextEmbeddingParam toEmbeddingParams(EmbeddingRequest requestOptions) { - - TextEmbeddingParam tongYiEmbeddingParams = TextEmbeddingParam.builder() - .texts(requestOptions.getInstructions()) - .textType(defaultOptions.getTextType() != null ? defaultOptions.getTextType() : TextEmbeddingParam.TextType.DOCUMENT) - .model("text-embedding-v1") - .build(); - - try { - tongYiEmbeddingParams.validate(); - } - catch (InputRequiredException e) { - throw new TongYiException(e.getMessage()); - } - - return tongYiEmbeddingParams; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java deleted file mode 100644 index 02196f41d8..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/embedding/TongYiTextEmbeddingProperties.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.embedding; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiTextEmbeddingProperties.CONFIG_PREFIX) -public class TongYiTextEmbeddingProperties { - - /** - * Prefix of TongYi Text Embedding properties. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "embedding"; - - private boolean enabled = true; - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java deleted file mode 100644 index 55dbb339dc..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesModel.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.image; - -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiImagesException; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; -import com.alibaba.dashscope.exception.NoApiKeyException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.image.*; -import org.springframework.util.Assert; - -import java.io.ByteArrayOutputStream; -import java.net.URL; -import java.util.Base64; -import java.util.stream.Collectors; - -import static com.alibaba.cloud.ai.tongyi.metadata.TongYiImagesResponseMetadata.from; - -/** - * TongYiImagesClient is a class that implements the ImageClient interface. It provides a - * client for calling the TongYi AI image generation API. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesModel implements ImageModel { - - private final Logger logger = LoggerFactory.getLogger(TongYiImagesModel.class); - - /** - * Gen Images API. - */ - private final ImageSynthesis imageSynthesis; - - /** - * TongYi Gen images properties. - */ - private TongYiImagesOptions defaultOptions; - - /** - * Adapt TongYi images api size properties. - */ - private final String sizeConnection = "*"; - - /** - * Get default images options. - * - * @return Default TongYiImagesOptions. - */ - public TongYiImagesOptions getDefaultOptions() { - - return this.defaultOptions; - } - - /** - * TongYiImagesClient constructor. - * @param imageSynthesis the image synthesis - * {@link ImageSynthesis} - */ - public TongYiImagesModel(ImageSynthesis imageSynthesis) { - - this(imageSynthesis, TongYiImagesOptions. - builder() - .withModel(ImageSynthesis.Models.WANX_V1) - .withN(1) - .build() - ); - } - - /** - * TongYiImagesClient constructor. - * @param imageSynthesis {@link ImageSynthesis} - * @param imagesOptions {@link TongYiImagesOptions} - */ - public TongYiImagesModel(ImageSynthesis imageSynthesis, TongYiImagesOptions imagesOptions) { - - Assert.notNull(imageSynthesis, "ImageSynthesis must not be null"); - Assert.notNull(imagesOptions, "TongYiImagesOptions must not be null"); - - this.imageSynthesis = imageSynthesis; - this.defaultOptions = imagesOptions; - } - - /** - * Call the TongYi images service. - * @param imagePrompt the image prompt. - * @return the image response. - * {@link ImageSynthesis#call(ImageSynthesisParam)} - */ - @Override - public ImageResponse call(ImagePrompt imagePrompt) { - - ImageSynthesisResult result; - String prompt = imagePrompt.getInstructions().get(0).getText(); - var imgParams = ImageSynthesisParam.builder() - .prompt("") - .model(ImageSynthesis.Models.WANX_V1) - .build(); - - if (this.defaultOptions != null) { - - imgParams = merge(this.defaultOptions); - } - - if (imagePrompt.getOptions() != null) { - - imgParams = merge(toTingYiImageOptions(imagePrompt.getOptions())); - } - imgParams.setPrompt(prompt); - - try { - result = imageSynthesis.call(imgParams); - } - catch (NoApiKeyException e) { - - logger.error("TongYi models gen images failed: {}.", e.getMessage()); - throw new TongYiImagesException(e.getMessage()); - } - - return convert(result); - } - - public ImageSynthesisParam merge(TongYiImagesOptions target) { - - var builder = ImageSynthesisParam.builder(); - - builder.model(this.defaultOptions.getModel() != null ? this.defaultOptions.getModel() : target.getModel()); - builder.n(this.defaultOptions.getN() != null ? this.defaultOptions.getN() : target.getN()); - builder.size((this.defaultOptions.getHeight() != null && this.defaultOptions.getWidth() != null) - ? this.defaultOptions.getHeight() + "*" + this.defaultOptions.getWidth() - : target.getHeight() + "*" + target.getWidth() - ); - - // prompt is marked non-null but is null. - builder.prompt(""); - - return builder.build(); - } - - private ImageResponse convert(ImageSynthesisResult result) { - - return new ImageResponse( - result.getOutput().getResults().stream() - .flatMap(value -> value.entrySet().stream()) - .map(entry -> { - String key = entry.getKey(); - String value = entry.getValue(); - try { - String base64Image = convertImageToBase64(value); - Image image = new Image(value, base64Image); - return new ImageGeneration(image); - } - catch (Exception e) { - throw new RuntimeException(e); - } - }) - .collect(Collectors.toList()), - from(result) - ); - } - - public TongYiImagesOptions toTingYiImageOptions(ImageOptions runtimeImageOptions) { - - var builder = TongYiImagesOptions.builder(); - - if (runtimeImageOptions != null) { - if (runtimeImageOptions.getN() != null) { - - builder.withN(runtimeImageOptions.getN()); - } - if (runtimeImageOptions.getModel() != null) { - - builder.withModel(runtimeImageOptions.getModel()); - } - if (runtimeImageOptions.getHeight() != null) { - - builder.withHeight(runtimeImageOptions.getHeight()); - } - if (runtimeImageOptions.getWidth() != null) { - - builder.withWidth(runtimeImageOptions.getWidth()); - } - - // todo ImagesParams. - } - - return builder.build(); - } - - /** - * Convert image to base64. - * @param imageUrl the image url. - * @return the base64 image. - * @throws Exception the exception. - */ - public String convertImageToBase64(String imageUrl) throws Exception { - - var url = new URL(imageUrl); - var inputStream = url.openStream(); - var outputStream = new ByteArrayOutputStream(); - var buffer = new byte[4096]; - int bytesRead; - - while ((bytesRead = inputStream.read(buffer)) != -1) { - outputStream.write(buffer, 0, bytesRead); - } - - var imageBytes = outputStream.toByteArray(); - - String base64Image = Base64.getEncoder().encodeToString(imageBytes); - - inputStream.close(); - outputStream.close(); - - return base64Image; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java deleted file mode 100644 index 412b07be04..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesOptions.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.image; - -import com.alibaba.cloud.ai.tongyi.common.exception.TongYiImagesException; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import org.springframework.ai.image.ImageOptions; - -import java.util.Objects; - -/** - * TongYi Image API options. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesOptions implements ImageOptions { - - /** - * Specify the model name, currently only wanx-v1 is supported. - */ - private String model = ImageSynthesis.Models.WANX_V1; - - /** - * Gen images number. - */ - private Integer n; - - /** - * The width of the generated images. - */ - private Integer width = 1024; - - /** - * The height of the generated images. - */ - private Integer height = 1024; - - @Override - public Integer getN() { - - return this.n; - } - - @Override - public String getModel() { - - return this.model; - } - - @Override - public Integer getWidth() { - - return this.width; - } - - @Override - public Integer getHeight() { - - return this.height; - } - - @Override - public String getResponseFormat() { - - throw new TongYiImagesException("unimplemented!"); - } - - public void setModel(String model) { - - this.model = model; - } - - public void setN(Integer n) { - - this.n = n; - } - - public void setWidth(Integer width) { - - this.width = width; - } - - public void setHeight(Integer height) { - - this.height = height; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - - return true; - } - if (o == null || getClass() != o.getClass()) { - - return false; - } - - TongYiImagesOptions that = (TongYiImagesOptions) o; - - return Objects.equals(model, that.model) - && Objects.equals(n, that.n) - && Objects.equals(width, that.width) - && Objects.equals(height, that.height); - } - - @Override - public int hashCode() { - - return Objects.hash(model, n, width, height); - } - - @Override - public String toString() { - - final StringBuilder sb = new StringBuilder("TongYiImagesOptions{"); - - sb.append("model='").append(model).append('\''); - sb.append(", n=").append(n); - sb.append(", width=").append(width); - sb.append(", height=").append(height); - sb.append('}'); - - return sb.toString(); - } - - public static Builder builder() { - return new Builder(); - } - - public final static class Builder { - - private final TongYiImagesOptions options; - - private Builder() { - this.options = new TongYiImagesOptions(); - } - - public Builder withN(Integer n) { - - options.setN(n); - return this; - } - - public Builder withModel(String model) { - - options.setModel(model); - return this; - } - - public Builder withWidth(Integer width) { - - options.setWidth(width); - return this; - } - - public Builder withHeight(Integer height) { - - options.setHeight(height); - return this; - } - - public TongYiImagesOptions build() { - - return options; - } - - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java deleted file mode 100644 index 23f4d7f33a..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/image/TongYiImagesProperties.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.image; - -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.NestedConfigurationProperty; - -import static com.alibaba.cloud.ai.tongyi.common.constants.TongYiConstants.SCA_AI_CONFIGURATION; - -/** - * TongYi Image API properties. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -@ConfigurationProperties(TongYiImagesProperties.CONFIG_PREFIX) -public class TongYiImagesProperties { - - /** - * Spring Cloud Alibaba AI configuration prefix. - */ - public static final String CONFIG_PREFIX = SCA_AI_CONFIGURATION + "images"; - - /** - * Default TongYi Chat model. - */ - public static final String DEFAULT_IMAGES_MODEL_NAME = ImageSynthesis.Models.WANX_V1; - - /** - * Enable TongYiQWEN ai images client. - */ - private boolean enabled = true; - - @NestedConfigurationProperty - private TongYiImagesOptions options = TongYiImagesOptions.builder() - .withModel(DEFAULT_IMAGES_MODEL_NAME) - .withN(1) - .build(); - - public TongYiImagesOptions getOptions() { - - return this.options; - } - - public void setOptions(TongYiImagesOptions options) { - - this.options = options; - } - - public boolean isEnabled() { - - return this.enabled; - } - - public void setEnabled(boolean enabled) { - - this.enabled = enabled; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java deleted file mode 100644 index 3918313c0d..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiChatResponseMetadata.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata; - -import com.alibaba.dashscope.aigc.generation.GenerationResult; -import org.springframework.ai.chat.metadata.ChatResponseMetadata; -import org.springframework.ai.chat.metadata.PromptMetadata; -import org.springframework.ai.chat.metadata.Usage; -import org.springframework.util.Assert; - -import java.util.HashMap; - - - -/** - * {@link ChatResponseMetadata} implementation for {@literal Alibaba DashScope}. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAiChatResponseMetadata extends HashMap implements ChatResponseMetadata { - - protected static final String AI_METADATA_STRING = "{ @type: %1$s, id: %2$s, usage: %3$s, rateLimit: %4$s }"; - - @SuppressWarnings("all") - public static TongYiAiChatResponseMetadata from(GenerationResult chatCompletions, - PromptMetadata promptFilterMetadata) { - - Assert.notNull(chatCompletions, "Alibaba ai ChatCompletions must not be null"); - String id = chatCompletions.getRequestId(); - TongYiAiUsage usage = TongYiAiUsage.from(chatCompletions); - - return new TongYiAiChatResponseMetadata( - id, - usage, - promptFilterMetadata - ); - } - - private final String id; - - private final Usage usage; - - private final PromptMetadata promptMetadata; - - protected TongYiAiChatResponseMetadata(String id, TongYiAiUsage usage, PromptMetadata promptMetadata) { - - this.id = id; - this.usage = usage; - this.promptMetadata = promptMetadata; - } - - public String getId() { - return this.id; - } - - @Override - public Usage getUsage() { - return this.usage; - } - - @Override - public PromptMetadata getPromptMetadata() { - return this.promptMetadata; - } - - @Override - public String toString() { - - return AI_METADATA_STRING.formatted(getClass().getTypeName(), getId(), getUsage(), getRateLimit()); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java deleted file mode 100644 index da086e502f..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiAiUsage.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata; - -import com.alibaba.dashscope.aigc.generation.GenerationResult; -import com.alibaba.dashscope.aigc.generation.GenerationUsage; -import org.springframework.ai.chat.metadata.Usage; -import org.springframework.util.Assert; - -/** - * {@link Usage} implementation for {@literal Alibaba DashScope}. - * - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAiUsage implements Usage { - - private final GenerationUsage usage; - - public TongYiAiUsage(GenerationUsage usage) { - - Assert.notNull(usage, "GenerationUsage must not be null"); - this.usage = usage; - } - - public static TongYiAiUsage from(GenerationResult chatCompletions) { - - Assert.notNull(chatCompletions, "ChatCompletions must not be null"); - return from(chatCompletions.getUsage()); - } - - public static TongYiAiUsage from(GenerationUsage usage) { - - return new TongYiAiUsage(usage); - } - - protected GenerationUsage getUsage() { - - return this.usage; - } - - @Override - public Long getPromptTokens() { - - throw new UnsupportedOperationException("Unimplemented method 'getPromptTokens'"); - } - - @Override - public Long getGenerationTokens() { - - return this.getUsage().getOutputTokens().longValue(); - } - - @Override - public Long getTotalTokens() { - - return this.getUsage().getTotalTokens().longValue(); - } - - @Override - public String toString() { - - return this.getUsage().toString(); - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java deleted file mode 100644 index 11b38f6eb5..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiImagesResponseMetadata.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata; - -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisTaskMetrics; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisUsage; -import org.springframework.ai.image.ImageResponseMetadata; -import org.springframework.util.Assert; - -import java.util.HashMap; -import java.util.Objects; - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiImagesResponseMetadata extends HashMap implements ImageResponseMetadata { - - private final Long created; - - private String taskId; - - private ImageSynthesisTaskMetrics metrics; - - private ImageSynthesisUsage usage; - - public static TongYiImagesResponseMetadata from(ImageSynthesisResult synthesisResult) { - - Assert.notNull(synthesisResult, "TongYiAiImageResponse must not be null"); - - return new TongYiImagesResponseMetadata( - System.currentTimeMillis(), - synthesisResult.getOutput().getTaskMetrics(), - synthesisResult.getOutput().getTaskId(), - synthesisResult.getUsage() - ); - } - - protected TongYiImagesResponseMetadata( - Long created, - ImageSynthesisTaskMetrics metrics, - String taskId, - ImageSynthesisUsage usage - ) { - - this.taskId = taskId; - this.metrics = metrics; - this.created = created; - this.usage = usage; - } - - public ImageSynthesisUsage getUsage() { - return usage; - } - - public void setUsage(ImageSynthesisUsage usage) { - this.usage = usage; - } - - @Override - public Long getCreated() { - return created; - } - - public String getTaskId() { - return taskId; - } - - public void setTaskId(String taskId) { - this.taskId = taskId; - } - - public ImageSynthesisTaskMetrics getMetrics() { - return metrics; - } - - void setMetrics(ImageSynthesisTaskMetrics metrics) { - this.metrics = metrics; - } - - - public Long created() { - return this.created; - } - - @Override - public String toString() { - return "TongYiImagesResponseMetadata {" + "created=" + created + '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - - return true; - } - if (o == null || getClass() != o.getClass()) { - - return false; - } - - TongYiImagesResponseMetadata that = (TongYiImagesResponseMetadata) o; - - return Objects.equals(created, that.created) - && Objects.equals(taskId, that.taskId) - && Objects.equals(metrics, that.metrics); - } - - @Override - public int hashCode() { - return Objects.hash(created, taskId, metrics); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java deleted file mode 100644 index 414154bf84..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/TongYiTextEmbeddingResponseMetadata.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata; - -import com.alibaba.dashscope.embeddings.TextEmbeddingUsage; -import org.springframework.ai.embedding.EmbeddingResponseMetadata; - -/** - * @author why_ohh - * @author yuluo - * @author why_ohh - * @since 2023.0.1.0 - */ - -public class TongYiTextEmbeddingResponseMetadata extends EmbeddingResponseMetadata { - - private Integer totalTokens; - - protected TongYiTextEmbeddingResponseMetadata(Integer totalTokens) { - - this.totalTokens = totalTokens; - } - - public static TongYiTextEmbeddingResponseMetadata from(TextEmbeddingUsage usage) { - - return new TongYiTextEmbeddingResponseMetadata(usage.getTotalTokens()); - } - - public Integer getTotalTokens() { - - return totalTokens; - } - - public void setTotalTokens(Integer totalTokens) { - - this.totalTokens = totalTokens; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java deleted file mode 100644 index 8647e3f0b8..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioSpeechResponseMetadata.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata.audio; - -import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult; -import com.alibaba.dashscope.audio.tts.SpeechSynthesisUsage; -import com.alibaba.dashscope.audio.tts.timestamp.Sentence; -import org.springframework.ai.chat.metadata.EmptyRateLimit; -import org.springframework.ai.chat.metadata.RateLimit; -import org.springframework.ai.model.ResponseMetadata; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -import java.util.HashMap; - - - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioSpeechResponseMetadata extends HashMap implements ResponseMetadata { - - private SpeechSynthesisUsage usage; - - private String requestId; - - private Sentence time; - - protected static final String AI_METADATA_STRING = "{ @type: %1$s, requestsLimit: %2$s }"; - - /** - * NULL objects. - */ - public static final TongYiAudioSpeechResponseMetadata NULL = new TongYiAudioSpeechResponseMetadata() { - }; - - public static TongYiAudioSpeechResponseMetadata from(SpeechSynthesisResult result) { - - Assert.notNull(result, "TongYi AI speech must not be null"); - TongYiAudioSpeechResponseMetadata speechResponseMetadata = new TongYiAudioSpeechResponseMetadata(); - - - - return speechResponseMetadata; - } - - public static TongYiAudioSpeechResponseMetadata from(String result) { - - Assert.notNull(result, "TongYi AI speech must not be null"); - TongYiAudioSpeechResponseMetadata speechResponseMetadata = new TongYiAudioSpeechResponseMetadata(); - - return speechResponseMetadata; - } - - @Nullable - private RateLimit rateLimit; - - public TongYiAudioSpeechResponseMetadata() { - - this(null); - } - - public TongYiAudioSpeechResponseMetadata(@Nullable RateLimit rateLimit) { - - this.rateLimit = rateLimit; - } - - @Nullable - public RateLimit getRateLimit() { - - RateLimit rateLimit = this.rateLimit; - return rateLimit != null ? rateLimit : new EmptyRateLimit(); - } - - public TongYiAudioSpeechResponseMetadata withRateLimit(RateLimit rateLimit) { - - this.rateLimit = rateLimit; - return this; - } - - public TongYiAudioSpeechResponseMetadata withUsage(SpeechSynthesisUsage usage) { - - this.usage = usage; - return this; - } - - public TongYiAudioSpeechResponseMetadata withRequestId(String id) { - - this.requestId = id; - return this; - } - - public TongYiAudioSpeechResponseMetadata withSentence(Sentence sentence) { - - this.time = sentence; - return this; - } - - public SpeechSynthesisUsage getUsage() { - return usage; - } - - public String getRequestId() { - return requestId; - } - - public Sentence getTime() { - return time; - } - - @Override - public String toString() { - return AI_METADATA_STRING.formatted(getClass().getName(), getRateLimit()); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java deleted file mode 100644 index 32d5d63c26..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionMetadata.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata.audio; - -import org.springframework.ai.model.ResultMetadata; - -/** - * @author xYLiu - * @author yuluo - * @since 2023.0.1.0 - */ - -public interface TongYiAudioTranscriptionMetadata extends ResultMetadata { - - /** - * A constant instance of {@link TongYiAudioTranscriptionMetadata} that represents a null or empty metadata. - */ - TongYiAudioTranscriptionMetadata NULL = TongYiAudioTranscriptionMetadata.create(); - - /** - * Factory method for creating a new instance of {@link TongYiAudioTranscriptionMetadata}. - * @return a new instance of {@link TongYiAudioTranscriptionMetadata} - */ - static TongYiAudioTranscriptionMetadata create() { - return new TongYiAudioTranscriptionMetadata() { - }; - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java deleted file mode 100644 index 36525d5f56..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/com/alibaba/cloud/ai/tongyi/metadata/audio/TongYiAudioTranscriptionResponseMetadata.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2023-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.alibaba.cloud.ai.tongyi.metadata.audio; - -import com.alibaba.dashscope.audio.asr.transcription.TranscriptionResult; -import com.google.gson.JsonObject; -import org.springframework.ai.chat.metadata.EmptyRateLimit; -import org.springframework.ai.chat.metadata.RateLimit; -import org.springframework.ai.model.ResponseMetadata; -import org.springframework.util.Assert; - -import javax.annotation.Nullable; -import java.util.HashMap; - - - -/** - * @author yuluo - * @author yuluo - * @since 2023.0.1.0 - */ - -public class TongYiAudioTranscriptionResponseMetadata extends HashMap implements ResponseMetadata { - - @Nullable - private RateLimit rateLimit; - - private JsonObject usage; - - protected static final String AI_METADATA_STRING = "{ @type: %1$s, rateLimit: %4$s }"; - - /** - * NULL objects. - */ - public static final TongYiAudioTranscriptionResponseMetadata NULL = new TongYiAudioTranscriptionResponseMetadata() { - }; - - protected TongYiAudioTranscriptionResponseMetadata() { - - this(null, new JsonObject()); - } - - protected TongYiAudioTranscriptionResponseMetadata(JsonObject usage) { - - this(null, usage); - } - - protected TongYiAudioTranscriptionResponseMetadata(@Nullable RateLimit rateLimit, JsonObject usage) { - - this.rateLimit = rateLimit; - this.usage = usage; - } - - public static TongYiAudioTranscriptionResponseMetadata from(TranscriptionResult result) { - - Assert.notNull(result, "TongYi Transcription must not be null"); - return new TongYiAudioTranscriptionResponseMetadata(result.getUsage()); - } - - @Nullable - public RateLimit getRateLimit() { - - return this.rateLimit != null ? this.rateLimit : new EmptyRateLimit(); - } - - public void setRateLimit(@Nullable RateLimit rateLimit) { - this.rateLimit = rateLimit; - } - - public JsonObject getUsage() { - return usage; - } - - public void setUsage(JsonObject usage) { - this.usage = usage; - } - - @Override - public String toString() { - - return AI_METADATA_STRING.formatted(getClass().getName(), getRateLimit()); - } - -} From 0400932260d5ba33f7e813a1daef8be9b0512506 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 20 Feb 2025 17:44:06 +0800 Subject: [PATCH 183/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E7=9A=84=E6=89=A7=E8=A1=8C=E5=99=A8=E6=8A=BD=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-spring-boot-starter-mq/pom.xml | 1 + yudao-module-iot/yudao-module-iot-biz/pom.xml | 7 +- .../action/IotRuleSceneDataBridgeAction.java | 157 +----------------- .../rule/execute/IotDataBridgeExecute.java | 32 ++++ .../execute/IotHttpDataBridgeExecute.java | 93 +++++++++++ .../execute/IotRocketMQDataBridgeExecute.java | 96 +++++++++++ 6 files changed, 232 insertions(+), 154 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java diff --git a/yudao-framework/yudao-spring-boot-starter-mq/pom.xml b/yudao-framework/yudao-spring-boot-starter-mq/pom.xml index 15bbaa42de..c8972f16b2 100644 --- a/yudao-framework/yudao-spring-boot-starter-mq/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mq/pom.xml @@ -36,6 +36,7 @@ org.apache.rocketmq rocketmq-spring-boot-starter + true diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 2a9669c0ee..ea1dde86fe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -75,10 +75,11 @@ - + - cn.iocoder.boot - yudao-spring-boot-starter-mq + org.apache.rocketmq + rocketmq-spring-boot-starter + true diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index 7668c05b94..f87cd60011 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -1,33 +1,18 @@ package cn.iocoder.yudao.module.iot.service.rule.action; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; +import cn.iocoder.yudao.module.iot.service.rule.execute.IotDataBridgeExecute; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.client.producer.SendResult; -import org.apache.rocketmq.client.producer.SendStatus; -import org.apache.rocketmq.common.message.Message; -import org.apache.rocketmq.remoting.common.RemotingHelper; -import org.springframework.http.*; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; +import java.util.List; /** * IoT 数据桥梁的 {@link IotRuleSceneAction} 实现类 @@ -41,11 +26,10 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ @Slf4j public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { - @Resource - private RestTemplate restTemplate; - @Resource private IotDataBridgeService dataBridgeService; + @Resource + private List dataBridgeExecutes; @Override public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { @@ -65,26 +49,8 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { return; } - // 2.1 执行 HTTP 请求 - if (IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { - executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig()); - return; - } - // 2.2 执行 RocketMQ 发送消息 - if (IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) { - executeRocketMQ(message, (IotDataBridgeDO.RocketMQConfig) dataBridge.getConfig()); - return; - } - - // TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; - // TODO @芋艿:mq-redis - // TODO @芋艿:mq-数据库 - // TODO @芋艿:kafka - // TODO @芋艿:rocketmq - // TODO @芋艿:rabbitmq - // TODO @芋艿:mqtt - // TODO @芋艿:tcp - // TODO @芋艿:websocket + // 2.1 执行数据桥接操作 + dataBridgeExecutes.forEach(execute -> execute.execute(message, dataBridge)); } @Override @@ -92,115 +58,4 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { return IotRuleSceneActionTypeEnum.DATA_BRIDGE; } - @SuppressWarnings({"unchecked", "deprecation"}) - private void executeHttp(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) { - String url = null; - HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); - HttpEntity requestEntity = null; - ResponseEntity responseEntity = null; - try { - // 1.1 构建 Header - HttpHeaders headers = new HttpHeaders(); - if (CollUtil.isNotEmpty(config.getHeaders())) { - config.getHeaders().putAll(config.getHeaders()); - } - headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); - // 1.2 构建 URL - UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); - if (CollUtil.isNotEmpty(config.getQuery())) { - config.getQuery().forEach(uriBuilder::queryParam); - } - // 1.3 构建请求体 - if (method == HttpMethod.GET) { - uriBuilder.queryParam("message", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message))); - url = uriBuilder.build().toUriString(); - requestEntity = new HttpEntity<>(headers); - } else { - url = uriBuilder.build().toUriString(); - Map requestBody = JsonUtils.parseObject(config.getBody(), Map.class); - if (requestBody == null) { - requestBody = new HashMap<>(); - } - requestBody.put("message", message); - headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); - requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); - } - - // 2.1 发送请求 - responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); - // 2.2 记录日志 - if (responseEntity.getStatusCode().is2xxSuccessful()) { - log.info("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", - message, config, url, method, requestEntity, responseEntity); - } else { - log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", - message, config, url, method, requestEntity, responseEntity); - } - } catch (Exception e) { - log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", - message, config, url, method, requestEntity, responseEntity, e); - } - } - - private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { - // 1.1 创建生产者实例,指定生产者组名 - DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); - // TODO @puhui999:可以考虑,基于 guava 做 cache,使用 config 作为 key,然后假个 listener 超时,销毁 producer - try { - // 1.2 设置 NameServer 地址 - producer.setNamesrvAddr(config.getNameServer()); - // 1.3 启动生产者 - producer.start(); - - // 2.1 创建消息对象,指定Topic、Tag和消息体 - Message msg = new Message( - config.getTopic(), - config.getTags(), - message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) - ); - // 2.2 发送同步消息并处理结果 - SendResult sendResult = producer.send(msg); - // 2.3 处理发送结果 - if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { - log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); - } else { - log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); - } - } catch (Exception e) { - log.error("[executeRocketMQ][message({}) config({}) 发送异常]", message, config, e); - } finally { - // 3. 关闭生产者 - producer.shutdown(); - } - } - - // TODO @芋艿:测试代码,后续清理 - public static void main(String[] args) { - // 1. 创建 IotRuleSceneDataBridgeAction 实例 - IotRuleSceneDataBridgeAction action = new IotRuleSceneDataBridgeAction(); - - // 2. 创建测试消息 - IotDeviceMessage message = IotDeviceMessage.builder() - .requestId("TEST-001") - .productKey("testProduct") - .deviceName("testDevice") - .deviceKey("testDeviceKey") - .type("property") - .identifier("temperature") - .data("{\"value\": 60}") - .reportTime(LocalDateTime.now()) - .tenantId(1L) - .build(); - - // 3. 创建 RocketMQ 配置 - IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig(); - config.setNameServer("127.0.0.1:9876"); - config.setGroup("test-group"); - config.setTopic("test-topic"); - config.setTags("test-tag"); - - // 4. 执行测试 - action.executeRocketMQ(message, config); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java new file mode 100644 index 0000000000..8979d21150 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.iot.service.rule.execute; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; + + +/** + * IoT 数据桥梁的执行器 execute 接口 + * + * @author HUIHUI + */ +public interface IotDataBridgeExecute { + + /** + * 执行数据桥接操作 + * + * @param message 设备消息 + * @param dataBridge 数据桥梁 + */ + void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge); + + // TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; + // TODO @芋艿:mq-redis + // TODO @芋艿:mq-数据库 + // TODO @芋艿:kafka + // TODO @芋艿:rocketmq + // TODO @芋艿:rabbitmq + // TODO @芋艿:mqtt + // TODO @芋艿:tcp + // TODO @芋艿:websocket + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java new file mode 100644 index 0000000000..861fadd6c0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.iot.service.rule.execute; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; + +/** + * Http 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { + + @Resource + private RestTemplate restTemplate; + + @Override + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥接的类型 == HTTP + if (!IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { + return; + } + // 1.2 执行 HTTP 请求 + executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig()); + } + + @SuppressWarnings({"unchecked", "deprecation"}) + private void executeHttp(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) { + String url = null; + HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); + HttpEntity requestEntity = null; + ResponseEntity responseEntity = null; + try { + // 1.1 构建 Header + HttpHeaders headers = new HttpHeaders(); + if (CollUtil.isNotEmpty(config.getHeaders())) { + config.getHeaders().putAll(config.getHeaders()); + } + headers.add(HEADER_TENANT_ID, message.getTenantId().toString()); + // 1.2 构建 URL + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(config.getUrl()); + if (CollUtil.isNotEmpty(config.getQuery())) { + config.getQuery().forEach(uriBuilder::queryParam); + } + // 1.3 构建请求体 + if (method == HttpMethod.GET) { + uriBuilder.queryParam("message", HttpUtils.encodeUtf8(JsonUtils.toJsonString(message))); + url = uriBuilder.build().toUriString(); + requestEntity = new HttpEntity<>(headers); + } else { + url = uriBuilder.build().toUriString(); + Map requestBody = JsonUtils.parseObject(config.getBody(), Map.class); + if (requestBody == null) { + requestBody = new HashMap<>(); + } + requestBody.put("message", message); + headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE); + requestEntity = new HttpEntity<>(JsonUtils.toJsonString(requestBody), headers); + } + + // 2.1 发送请求 + responseEntity = restTemplate.exchange(url, method, requestEntity, String.class); + // 2.2 记录日志 + if (responseEntity.getStatusCode().is2xxSuccessful()) { + log.info("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求成功({})]", + message, config, url, method, requestEntity, responseEntity); + } else { + log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求失败({})]", + message, config, url, method, requestEntity, responseEntity); + } + } catch (Exception e) { + log.error("[executeHttp][message({}) config({}) url({}) method({}) requestEntity({}) 请求异常({})]", + message, config, url, method, requestEntity, responseEntity, e); + } + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java new file mode 100644 index 0000000000..3e02e6960a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.iot.service.rule.execute; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.common.message.Message; +import org.apache.rocketmq.remoting.common.RemotingHelper; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * RocketMQ 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { + + @Override + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥接的类型 == ROCKETMQ + if (!IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) { + return; + } + // 1.2 执行 RocketMQ 发送消息 + executeRocketMQ(message, (IotDataBridgeDO.RocketMQConfig) dataBridge.getConfig()); + } + + private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { + // 1.1 创建生产者实例,指定生产者组名 + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); + // TODO @puhui999:可以考虑,基于 guava 做 cache,使用 config 作为 key,然后假个 listener 超时,销毁 producer + try { + // 1.2 设置 NameServer 地址 + producer.setNamesrvAddr(config.getNameServer()); + // 1.3 启动生产者 + producer.start(); + + // 2.1 创建消息对象,指定Topic、Tag和消息体 + Message msg = new Message( + config.getTopic(), + config.getTags(), + message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) + ); + // 2.2 发送同步消息并处理结果 + SendResult sendResult = producer.send(msg); + // 2.3 处理发送结果 + if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { + log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); + } else { + log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); + } + } catch (Exception e) { + log.error("[executeRocketMQ][message({}) config({}) 发送异常]", message, config, e); + } finally { + // 3. 关闭生产者 + producer.shutdown(); + } + } + + // TODO @芋艿:测试代码,后续清理 + public static void main(String[] args) { + // 1. 创建 IotRocketMQDataBridgeExecute 实例 + IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); + + // 2. 创建测试消息 + IotDeviceMessage message = IotDeviceMessage.builder() + .requestId("TEST-001") + .productKey("testProduct") + .deviceName("testDevice") + .deviceKey("testDeviceKey") + .type("property") + .identifier("temperature") + .data("{\"value\": 60}") + .reportTime(LocalDateTime.now()) + .tenantId(1L) + .build(); + + // 3. 创建 RocketMQ 配置 + IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig(); + config.setNameServer("127.0.0.1:9876"); + config.setGroup("test-group"); + config.setTopic("test-topic"); + config.setTags("test-tag"); + + // 4. 执行测试 + action.executeRocketMQ(message, config); + } + +} From 4be18af236ac036741c6adba23fa58d06f17a73c Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 20 Feb 2025 18:21:52 +0800 Subject: [PATCH 184/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E5=9F=BA=E4=BA=8E=20guava=20=E5=AF=B9?= =?UTF-8?q?=20producer=20=E5=81=9A=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../execute/IotRocketMQDataBridgeExecute.java | 69 ++++++++++++++----- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java index 3e02e6960a..11ca2339cf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java @@ -3,6 +3,9 @@ package cn.iocoder.yudao.module.iot.service.rule.execute; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; @@ -11,7 +14,9 @@ import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; import org.springframework.stereotype.Component; +import java.time.Duration; import java.time.LocalDateTime; +import java.util.concurrent.Executors; /** * RocketMQ 的 {@link IotDataBridgeExecute} 实现类 @@ -22,6 +27,36 @@ import java.time.LocalDateTime; @Slf4j public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { + /** + * 针对 {@link IotDataBridgeDO.RocketMQConfig} 的 DefaultMQProducer 缓存 + */ + private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() + // 只阻塞当前数据加载线程,其他线程返回旧值 + .refreshAfterWrite(Duration.ofMinutes(10)) + // 增加移除监听器,自动关闭 producer + .removalListener(notification -> { + DefaultMQProducer producer = (DefaultMQProducer) notification.getValue(); + if (producer != null) { + try { + producer.shutdown(); + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey()); + } catch (Exception e) { + log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e); + } + } + }) + // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 + .build(CacheLoader.asyncReloading(new CacheLoader() { + @Override + public DefaultMQProducer load(IotDataBridgeDO.RocketMQConfig config) throws Exception { + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); + producer.setNamesrvAddr(config.getNameServer()); + producer.start(); + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); + return producer; + } + }, Executors.newCachedThreadPool())); + @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { // 1.1 校验数据桥接的类型 == ROCKETMQ @@ -33,14 +68,9 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { } private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { - // 1.1 创建生产者实例,指定生产者组名 - DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); - // TODO @puhui999:可以考虑,基于 guava 做 cache,使用 config 作为 key,然后假个 listener 超时,销毁 producer try { - // 1.2 设置 NameServer 地址 - producer.setNamesrvAddr(config.getNameServer()); - // 1.3 启动生产者 - producer.start(); + // 1. 获取或创建 Producer + DefaultMQProducer producer = PRODUCER_CACHE.get(config); // 2.1 创建消息对象,指定Topic、Tag和消息体 Message msg = new Message( @@ -58,18 +88,22 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { } } catch (Exception e) { log.error("[executeRocketMQ][message({}) config({}) 发送异常]", message, config, e); - } finally { - // 3. 关闭生产者 - producer.shutdown(); } } // TODO @芋艿:测试代码,后续清理 public static void main(String[] args) { - // 1. 创建 IotRocketMQDataBridgeExecute 实例 + // 1. 创建一个共享的实例 IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); - // 2. 创建测试消息 + // 2. 创建共享的配置 + IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig(); + config.setNameServer("127.0.0.1:9876"); + config.setGroup("test-group"); + config.setTopic("test-topic"); + config.setTags("test-tag"); + + // 3. 创建共享的消息 IotDeviceMessage message = IotDeviceMessage.builder() .requestId("TEST-001") .productKey("testProduct") @@ -82,14 +116,11 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { .tenantId(1L) .build(); - // 3. 创建 RocketMQ 配置 - IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig(); - config.setNameServer("127.0.0.1:9876"); - config.setGroup("test-group"); - config.setTopic("test-topic"); - config.setTags("test-tag"); + // 4. 执行两次测试,验证缓存 + log.info("[main][第一次执行,应该会创建新的 producer]"); + action.executeRocketMQ(message, config); - // 4. 执行测试 + log.info("[main][第二次执行,应该会复用缓存的 producer]"); action.executeRocketMQ(message, config); } From ca9575226637284b00fc07545b0b1082267aa743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Thu, 20 Feb 2025 18:30:57 +0800 Subject: [PATCH 185/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=B7=BB=E5=8A=A0=20EMQX=20=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E8=AE=BE=E5=A4=87=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E8=AE=A4=E8=AF=81=E5=92=8C=20MQTT=20=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=8F=82=E6=95=B0=E8=8E=B7=E5=8F=96=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/api/device/IotDeviceUpstreamApi.java | 8 ++ .../upstream/IotDeviceEmqxAuthReqDTO.java | 32 +++++ .../api/device/IoTDeviceUpstreamApiImpl.java | 11 +- .../admin/device/IotDeviceController.java | 9 +- .../iot/service/device/IotDeviceService.java | 8 ++ .../service/device/IotDeviceServiceImpl.java | 48 +++---- .../control/IotDeviceUpstreamService.java | 7 ++ .../control/IotDeviceUpstreamServiceImpl.java | 54 ++++++-- .../yudao/module/iot/util/MqttSignUtils.java | 96 ++++++++++++++ .../IotPluginCommonAutoConfiguration.java | 4 +- .../config/IotPluginCommonProperties.java | 11 ++ .../IotDeviceConfigSetVertxHandler.java | 2 +- .../IotDeviceOtaUpgradeVertxHandler.java | 7 +- .../IotDevicePropertyGetVertxHandler.java | 2 +- .../IotDevicePropertySetVertxHandler.java | 2 +- .../IotDeviceServiceInvokeVertxHandler.java | 5 +- .../IotPluginInstanceHeartbeatJob.java | 5 +- .../upstream/IotDeviceUpstreamClient.java | 6 + .../common/util/IotPluginCommonUtils.java | 8 ++ .../plugin.properties | 8 +- .../yudao-module-iot-plugin-emqx/pom.xml | 118 +++++++++--------- .../yudao/module/iot/plugin/EmqxPlugin.java | 42 ------- .../plugin/emqx/IotEmqxPluginApplication.java | 22 ++++ .../iot/plugin/emqx/config/IotEmqxPlugin.java | 57 +++++++++ .../IotPluginEmqxAutoConfiguration.java | 35 ++++++ .../emqx/config/IotPluginEmqxProperties.java | 42 +++++++ .../IotDeviceDownstreamHandlerImpl.java | 42 +++++++ .../upstream/IotDeviceUpstreamServer.java | 83 ++++++++++++ .../router/IotDeviceAuthVertxHandler.java | 54 ++++++++ .../src/main/resources/application.yml | 17 +++ .../IotDeviceEventReportVertxHandler.java | 2 +- .../src/main/resources/application.yml | 1 + 32 files changed, 685 insertions(+), 163 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/IotEmqxPluginApplication.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index 7d198eba3b..c43a0f2b23 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -71,6 +71,14 @@ public interface IotDeviceUpstreamApi { @PostMapping(PREFIX + "/add-topology") CommonResult addDeviceTopology(@Valid @RequestBody IotDeviceTopologyAddReqDTO addReqDTO); + /** + * 认证 Emqx 连接 + * + * @param authReqDTO 认证 Emqx 连接 DTO + */ + @PostMapping(PREFIX + "/authenticate-emqx-connection") + CommonResult authenticateEmqxConnection(@Valid @RequestBody IotDeviceEmqxAuthReqDTO authReqDTO); + // ========== 插件相关 ========== /** diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java new file mode 100644 index 0000000000..365552db0f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +/** + * IoT 认证 Emqx 连接 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDeviceEmqxAuthReqDTO { + + /** + * 客户端 ID + */ + @NotEmpty(message = "客户端 ID 不能为空") + private String clientId; + + /** + * 用户名 + */ + @NotEmpty(message = "用户名不能为空") + private String username; + + /** + * 密码 + */ + @NotEmpty(message = "密码不能为空") + private String password; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index 699f3dd754..61df8e43e4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -4,15 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.*; import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService; import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; +import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; - import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; /** - * * 设备数据 Upstream 上行 API 实现类 + * * 设备数据 Upstream 上行 API 实现类 */ @RestController @Validated @@ -61,6 +60,12 @@ public class IoTDeviceUpstreamApiImpl implements IotDeviceUpstreamApi { return success(true); } + @Override + public CommonResult authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { + Boolean result = deviceUpstreamService.authenticateEmqxConnection(authReqDTO); + return success(result); + } + // ========== 插件相关 ========== @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index ac2c6ebd83..18aa5a34fa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceDownstreamReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceUpstreamReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceDownstreamService; @@ -177,4 +177,11 @@ public class IotDeviceController { return success(true); } + @GetMapping("/mqtt-connection-params") + @Operation(summary = "获取 MQTT 连接参数") + @PreAuthorize("@ss.hasPermission('iot:device:mqtt-connection-params')") + public CommonResult getMqttConnectionParams(@RequestParam("deviceId") Long deviceId) { + return success(deviceService.getMqttConnectionParams(deviceId)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 24532d2548..1d73c6aed2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -168,4 +168,12 @@ public interface IotDeviceService { */ IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport); + /** + * 获取 MQTT 连接参数 + * + * @param deviceId 设备 ID + * @return MQTT 连接参数 + */ + IotDeviceMqttConnectionParamsRespVO getMqttConnectionParams(Long deviceId); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 66854e84b8..847581512b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.iot.dal.redis.RedisKeyConstants; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; import cn.iocoder.yudao.module.iot.service.product.IotProductService; +import cn.iocoder.yudao.module.iot.util.MqttSignUtils; +import cn.iocoder.yudao.module.iot.util.MqttSignUtils.MqttSignResult; import jakarta.annotation.Resource; import jakarta.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; @@ -123,10 +125,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { .setDeviceType(product.getDeviceType()); // 生成并设置必要的字段 // TODO @芋艿:各种 mqtt 是不是可以简化! - device.setDeviceSecret(generateDeviceSecret()) - .setMqttClientId(generateMqttClientId()) - .setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey())) - .setMqttPassword(generateMqttPassword()); + // clientId、username、password 根据规则实时生成 + device.setDeviceSecret(generateDeviceSecret()); // 设置设备状态为未激活 device.setState(IotDeviceStateEnum.INACTIVE.getState()); } @@ -318,35 +318,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { return IdUtil.fastSimpleUUID(); } - /** - * 生成 MQTT Client ID - * - * @return 生成的 MQTT Client ID - */ - private String generateMqttClientId() { - return IdUtil.fastSimpleUUID(); - } - - /** - * 生成 MQTT Username - * - * @param deviceName 设备名称 - * @param productKey 产品 Key - * @return 生成的 MQTT Username - */ - private String generateMqttUsername(String deviceName, String productKey) { - return deviceName + "&" + productKey; - } - - /** - * 生成 MQTT Password - * - * @return 生成的 MQTT Password - */ - private String generateMqttPassword() { - return RandomUtil.randomString(32); - } - @Override @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 public IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport) { @@ -417,6 +388,17 @@ public class IotDeviceServiceImpl implements IotDeviceService { return respVO; } + @Override + public IotDeviceMqttConnectionParamsRespVO getMqttConnectionParams(Long deviceId) { + IotDeviceDO device = validateDeviceExists(deviceId); + MqttSignResult mqttSignResult = MqttSignUtils.calculate(device.getProductKey(), device.getDeviceName(), + device.getDeviceSecret()); + return new IotDeviceMqttConnectionParamsRespVO() + .setMqttClientId(mqttSignResult.getClientId()) + .setMqttUsername(mqttSignResult.getUsername()) + .setMqttPassword(mqttSignResult.getPassword()); + } + private void deleteDeviceCache(IotDeviceDO device) { // 保证 Spring AOP 触发 getSelf().deleteDeviceCache0(device); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index 39b0d19c05..cb2e5b4003 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -62,4 +62,11 @@ public interface IotDeviceUpstreamService { */ void addDeviceTopology(IotDeviceTopologyAddReqDTO addReqDTO); + /** + * Emqx 连接认证 + * + * @param authReqDTO Emqx 连接认证 DTO + */ + Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO); + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index ad065f930e..93b4ea3888 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -20,6 +20,8 @@ import cn.iocoder.yudao.module.iot.mq.producer.device.IotDeviceProducer; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.data.IotDevicePropertyService; import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInstanceService; +import cn.iocoder.yudao.module.iot.util.MqttSignUtils; +import cn.iocoder.yudao.module.iot.util.MqttSignUtils.MqttSignResult; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -58,25 +60,26 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { // 2.1 情况一:属性上报 String requestId = IdUtil.fastSimpleUUID(); if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) { - reportDeviceProperty(((IotDevicePropertyReportReqDTO) - new IotDevicePropertyReportReqDTO().setRequestId(requestId).setReportTime(LocalDateTime.now()) - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) + reportDeviceProperty(((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO() + .setRequestId(requestId).setReportTime(LocalDateTime.now()) + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) .setProperties((Map) simulatorReqVO.getData())); return; } // 2.2 情况二:事件上报 if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.EVENT.getType())) { - reportDeviceEvent(((IotDeviceEventReportReqDTO) - new IotDeviceEventReportReqDTO().setRequestId(requestId).setReportTime(LocalDateTime.now()) - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) - .setIdentifier(simulatorReqVO.getIdentifier()).setParams((Map) simulatorReqVO.getData())); + reportDeviceEvent(((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) + .setReportTime(LocalDateTime.now()) + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) + .setIdentifier(simulatorReqVO.getIdentifier()) + .setParams((Map) simulatorReqVO.getData())); return; } // 2.3 情况三:状态变更 if (Objects.equals(simulatorReqVO.getType(), IotDeviceMessageTypeEnum.STATE.getType())) { - updateDeviceState(((IotDeviceStateUpdateReqDTO) - new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()).setReportTime(LocalDateTime.now()) - .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) + updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO() + .setRequestId(IdUtil.fastSimpleUUID()).setReportTime(LocalDateTime.now()) + .setProductKey(device.getProductKey()).setDeviceName(device.getDeviceName())) .setState((Integer) simulatorReqVO.getData())); return; } @@ -277,6 +280,37 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { sendDeviceMessage(message, device); } + @Override + public Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { + log.info("[authenticateEmqxConnection][认证 Emqx 连接: {}]", authReqDTO); + // 1. 校验设备是否存在 + // username 格式:${DeviceName}&${ProductKey} + String[] usernameParts = authReqDTO.getUsername().split("&"); + if (usernameParts.length != 2) { + log.error("[authenticateEmqxConnection][认证失败,username 格式不正确]"); + return Boolean.FALSE; + } + String deviceName = usernameParts[0]; + String productKey = usernameParts[1]; + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( + productKey, deviceName); + if (device == null) { + log.error("[authenticateEmqxConnection][设备({}/{}) 不存在]", + productKey, deviceName); + return Boolean.FALSE; + } + // 2. 校验密码 + String deviceSecret = device.getDeviceSecret(); + String clientId = authReqDTO.getClientId(); + MqttSignResult sign = MqttSignUtils.calculate(productKey, deviceName, deviceSecret, clientId); + if (!StrUtil.equals(sign.getPassword(), authReqDTO.getPassword())) { + log.error("[authenticateEmqxConnection][认证失败,密码不正确]"); + return Boolean.FALSE; + } + log.info("[authenticateEmqxConnection][认证成功]"); + return Boolean.TRUE; + } + private void updateDeviceLastTime(IotDeviceDO device, IotDeviceUpstreamAbstractReqDTO reqDTO) { // 1. 【异步】记录设备与插件实例的映射 pluginInstanceService.updateDevicePluginInstanceProcessIdAsync(device.getDeviceKey(), reqDTO.getProcessId()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java new file mode 100644 index 0000000000..40213e3aee --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.iot.util; + +import lombok.Getter; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; + +/** + * MQTT 签名工具类 + * 提供静态方法来计算 MQTT 连接参数。 + */ +public class MqttSignUtils { + + private static final String SIGN_METHOD = "hmacsha256"; + + /** + * 计算 MQTT 连接参数 + * + * @param productKey 产品密钥 + * @param deviceName 设备名称 + * @param deviceSecret 设备密钥 + * @return 包含 clientId, username, password 的结果对象 + */ + public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret) { + String clientId = productKey + "." + deviceName; + String username = deviceName + "&" + productKey; + String signContent = String.format("clientId%sdeviceName%sdeviceSecret%sproductKey%s", + clientId, deviceName, deviceSecret, productKey); + + String password = sign(signContent, deviceSecret); + + return new MqttSignResult(clientId, username, password); + } + + /** + * 计算 MQTT 连接参数 + * + * @param productKey 产品密钥 + * @param deviceName 设备名称 + * @param deviceSecret 设备密钥 + * @param clientId 客户端 ID + * @return 包含 clientId, username, password 的结果对象 + */ + public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret, String clientId) { + String username = deviceName + "&" + productKey; + String signContentBuilder = "clientId" + clientId + + "deviceName" + deviceName + + "deviceSecret" + deviceSecret + + "productKey" + productKey; + + String password = sign(signContentBuilder, deviceSecret); + + return new MqttSignResult(clientId, username, password); + } + + private static String sign(String content, String key) { + try { + Mac mac = Mac.getInstance(SIGN_METHOD); + mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), SIGN_METHOD)); + byte[] signData = mac.doFinal(content.getBytes(StandardCharsets.UTF_8)); + return bytesToHex(signData); + } catch (Exception e) { + throw new RuntimeException("Failed to sign content with HmacSHA256", e); + } + } + + private static String bytesToHex(byte[] bytes) { + StringBuilder hexString = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + String hex = Integer.toHexString(0xFF & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + /** + * MQTT 签名结果类 + */ + @Getter + public static class MqttSignResult { + private final String clientId; + private final String username; + private final String password; + + public MqttSignResult(String clientId, String username, String password) { + this.clientId = clientId; + this.username = username; + this.password = password; + } + + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java index 7e3d669f20..111189875d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java @@ -44,8 +44,8 @@ public class IotPluginCommonAutoConfiguration { @Bean(initMethod = "init", destroyMethod = "stop") public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob( - IotDeviceUpstreamApi deviceDataApi, IotDeviceDownstreamServer deviceDownstreamServer) { - return new IotPluginInstanceHeartbeatJob(deviceDataApi, deviceDownstreamServer); + IotDeviceUpstreamApi deviceDataApi, IotDeviceDownstreamServer deviceDownstreamServer, IotPluginCommonProperties commonProperties) { + return new IotPluginInstanceHeartbeatJob(deviceDataApi, deviceDownstreamServer, commonProperties); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java index 556786507c..03d42c2884 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonProperties.java @@ -7,6 +7,11 @@ import org.springframework.validation.annotation.Validated; import java.time.Duration; +/** + * IoT 插件的通用配置类 + * + * @author haohao + */ @ConfigurationProperties(prefix = "yudao.iot.plugin.common") @Validated @Data @@ -45,4 +50,10 @@ public class IotPluginCommonProperties { */ private Integer downstreamPort = DOWNSTREAM_PORT_RANDOM; + /** + * 插件包标识符 + */ + @NotEmpty(message = "插件包标识符不能为空") + private String pluginKey; + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java index 337db248f6..5051965b2f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; /** - * IOT 设备配置设置 Vertx Handler + * IoT 设备配置设置 Vertx Handler * * 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java index f81d385dc6..0d52dad498 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java @@ -5,14 +5,19 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceOt import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import io.vertx.core.json.JsonObject; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; +/** + * IoT 设备 OTA 升级 Vertx Handler + *

+ * 芋道源码 + */ @Slf4j @RequiredArgsConstructor public class IotDeviceOtaUpgradeVertxHandler implements Handler { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java index 5f9906fdc2..2e99a1b626 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; /** - * IOT 设备服务获取 Vertx Handler + * IoT 设备服务获取 Vertx Handler * * 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java index c8a60c7708..c3a71ff806 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; /** - * IOT 设备服务设置 Vertx Handler + * IoT 设备服务设置 Vertx Handler * * 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java index 421fe7484a..c4fd2e5044 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java @@ -5,17 +5,18 @@ import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceSe import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import io.vertx.core.json.JsonObject; + import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; /** - * IOT 设备服务调用 Vertx Handler + * IoT 设备服务调用 Vertx Handler * * 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java index 238d34f98a..f272468c56 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/heartbeat/IotPluginInstanceHeartbeatJob.java @@ -4,6 +4,7 @@ import cn.hutool.system.SystemUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotPluginInstanceHeartbeatReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import lombok.RequiredArgsConstructor; @@ -23,6 +24,7 @@ public class IotPluginInstanceHeartbeatJob { private final IotDeviceUpstreamApi deviceUpstreamApi; private final IotDeviceDownstreamServer deviceDownstreamServer; + private final IotPluginCommonProperties commonProperties; public void init() { CommonResult result = deviceUpstreamApi.heartbeatPluginInstance(buildPluginInstanceHeartbeatReqDTO(true)); @@ -41,9 +43,8 @@ public class IotPluginInstanceHeartbeatJob { } private IotPluginInstanceHeartbeatReqDTO buildPluginInstanceHeartbeatReqDTO(Boolean online) { - // TODO @haohao:pluginKey 的获取??? return new IotPluginInstanceHeartbeatReqDTO() - .setPluginKey("yudao-module-iot-plugin-http").setProcessId(IotPluginCommonUtils.getProcessId()) + .setPluginKey(commonProperties.getPluginKey()).setProcessId(IotPluginCommonUtils.getProcessId()) .setHostIp(SystemUtil.getHostInfo().getAddress()).setDownstreamPort(deviceDownstreamServer.getPort()) .setOnline(online); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java index ec662510ba..1bf4d676c0 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/upstream/IotDeviceUpstreamClient.java @@ -57,6 +57,12 @@ public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { return null; } + @Override + public CommonResult authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { + String url = properties.getUpstreamUrl() + URL_PREFIX + "/authenticate-emqx-connection"; + return doPost(url, authReqDTO); + } + @Override public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { String url = properties.getUpstreamUrl() + URL_PREFIX + "/report-property"; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java index f93717386e..a632c73c70 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java @@ -41,4 +41,12 @@ public class IotPluginCommonUtils { .end(JsonUtils.toJsonString(result)); } + @SuppressWarnings("deprecation") + public static void writeJson(RoutingContext routingContext, String result) { + routingContext.response() + .setStatusCode(200) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .end(result); + } + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties index 7f565b75e9..565e81eb06 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/plugin.properties @@ -1,6 +1,6 @@ -plugin.id=plugin-emqx -plugin.class=cn.iocoder.yudao.module.iot.plugin.EmqxPlugin +plugin.id=yudao-module-iot-plugin-emqx +plugin.class=cn.iocoder.yudao.module.iot.plugin.emqx.config.IotEmqxPlugin plugin.version=1.0.0 -plugin.provider=ahh +plugin.provider=yudao plugin.dependencies= -plugin.description=plugin-emqx-1.0.0 +plugin.description=yudao-module-iot-plugin-emqx-1.0.0 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml index cd89743214..34cb91d545 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml @@ -12,6 +12,7 @@ jar yudao-module-iot-plugin-emqx + 1.0.0 ${project.artifactId} @@ -21,36 +22,16 @@ emqx-plugin - cn.iocoder.yudao.module.iot.plugin.EmqxPlugin - 0.0.1 - ahh - emqx-plugin-0.0.1 + cn.iocoder.yudao.module.iot.plugin.emqx.config.IotEmqxPlugin + ${project.version} + yudao + ${project.artifactId}-${project.version} - - + org.apache.maven.plugins maven-antrun-plugin @@ -94,6 +75,7 @@ + org.apache.maven.plugins maven-jar-plugin @@ -111,54 +93,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - maven-deploy-plugin - - true - + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + -standalone + + + + - + + cn.iocoder.boot + yudao-module-iot-plugin-common + ${revision} + + + org.springframework.boot spring-boot-starter-web - - - org.pf4j - pf4j-spring - provided - - - - cn.iocoder.boot - yudao-module-iot-api - ${revision} - - - org.projectlombok - lombok - ${lombok.version} - provided - - - - io.vertx - vertx-core - - + + io.vertx vertx-web - - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 - \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java deleted file mode 100644 index b5fed5518b..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/EmqxPlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin; - -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import lombok.extern.slf4j.Slf4j; -import org.pf4j.Plugin; -import org.pf4j.PluginWrapper; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -@Slf4j -public class EmqxPlugin extends Plugin { - - private ExecutorService executorService; - - public EmqxPlugin(PluginWrapper wrapper) { - super(wrapper); - this.executorService = Executors.newSingleThreadExecutor(); - } - - @Override - public void start() { - log.info("EmqxPlugin.start()"); - - if (executorService.isShutdown() || executorService.isTerminated()) { - executorService = Executors.newSingleThreadExecutor(); - } - - IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class); - if (deviceDataApi == null) { - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); - return; - } - - } - - @Override - public void stop() { - log.info("EmqxPlugin.stop()"); - } -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/IotEmqxPluginApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/IotEmqxPluginApplication.java new file mode 100644 index 0000000000..1780384175 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/IotEmqxPluginApplication.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * IoT Emqx 插件的独立运行入口 + */ +@Slf4j +@SpringBootApplication +public class IotEmqxPluginApplication { + + public static void main(String[] args) { + SpringApplication application = new SpringApplication(IotEmqxPluginApplication.class); + application.setWebApplicationType(WebApplicationType.NONE); + application.run(args); + log.info("[main][独立模式启动完成]"); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java new file mode 100644 index 0000000000..af2e568628 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.config; + +import cn.hutool.extra.spring.SpringUtil; +import lombok.extern.slf4j.Slf4j; +import org.pf4j.PluginWrapper; +import org.pf4j.spring.SpringPlugin; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +// TODO @芋艿:完善注释 + +/** + * 负责插件的启动和停止 + */ +@Slf4j +public class IotEmqxPlugin extends SpringPlugin { + + public IotEmqxPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + log.info("[EmqxPlugin][EmqxPlugin 插件启动开始...]"); + try { + + log.info("[EmqxPlugin][EmqxPlugin 插件启动成功...]"); + } catch (Exception e) { + log.error("[EmqxPlugin][EmqxPlugin 插件开启动异常...]", e); + } + } + + @Override + public void stop() { + log.info("[EmqxPlugin][EmqxPlugin 插件停止开始...]"); + try { + log.info("[EmqxPlugin][EmqxPlugin 插件停止成功...]"); + } catch (Exception e) { + log.error("[EmqxPlugin][EmqxPlugin 插件停止异常...]", e); + } + } + + @Override + protected ApplicationContext createApplicationContext() { + // 创建插件自己的 ApplicationContext + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext(); + // 设置父容器为主应用的 ApplicationContext (确保主应用中提供的类可用) + pluginContext.setParent(SpringUtil.getApplicationContext()); + // 继续使用插件自己的 ClassLoader 以加载插件内部的类 + pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); + // 扫描当前插件的自动配置包 + pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.emqx.config"); + pluginContext.refresh(); + return pluginContext; + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java new file mode 100644 index 0000000000..4b9b104aa2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.config; + +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; +import cn.iocoder.yudao.module.iot.plugin.emqx.downstream.IotDeviceDownstreamHandlerImpl; +import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.IotDeviceUpstreamServer; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * IoT 插件 Emqx 的专用自动配置类 + * + * @author haohao + */ +@Configuration +@EnableConfigurationProperties(IotPluginEmqxProperties.class) +public class IotPluginEmqxAutoConfiguration { + + @Bean(initMethod = "start", destroyMethod = "stop") + public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, + IotPluginCommonProperties commonProperties, + IotPluginEmqxProperties emqxProperties, + IotDeviceDownstreamServer deviceDownstreamServer) { + return new IotDeviceUpstreamServer(commonProperties, emqxProperties, deviceUpstreamApi, deviceDownstreamServer); + } + + @Bean + public IotDeviceDownstreamHandler deviceDownstreamHandler() { + return new IotDeviceDownstreamHandlerImpl(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java new file mode 100644 index 0000000000..9004e864ef --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +/** + * 物联网插件 - EMQX 配置 + * + * @author 芋道源码 + */ +@ConfigurationProperties(prefix = "yudao.iot.plugin.emqx") +@Validated +@Data +public class IotPluginEmqxProperties { + + /** + * 服务主机 + */ + private String host; + + /** + * 服务端口 + */ + private int port; + + /** + * 是否启用 SSL + */ + private boolean ssl; + + /** + * 订阅的主题 + */ + private String topics; + + /** + * 认证端口 + */ + private int authPort; + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java new file mode 100644 index 0000000000..1d3ccba3ab --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.downstream; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; + +/** + * EMQX 插件的 {@link IotDeviceDownstreamHandler} 实现类 + *

+ * 但是:由于设备通过 HTTP 短链接接入,导致其实无法下行指导给 device 设备,所以基本都是直接返回失败!!! + * 类似 MQTT、WebSocket、TCP 插件,是可以实现下行指令的。 + * + * @author 芋道源码 + */ +public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { + + @Override + public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { + return CommonResult.success(true); + } + + @Override + public CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { + return CommonResult.success(true); + } + + @Override + public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { + return CommonResult.success(true); + } + + @Override + public CommonResult setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) { + return CommonResult.success(true); + } + + @Override + public CommonResult upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) { + return CommonResult.success(true); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java new file mode 100644 index 0000000000..49fdbe499f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.upstream; + +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.plugin.common.config.IotPluginCommonProperties; +import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamServer; +import cn.iocoder.yudao.module.iot.plugin.emqx.config.IotPluginEmqxProperties; +import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceAuthVertxHandler; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpServer; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import lombok.extern.slf4j.Slf4j; + +/** + * IoT 设备下行服务端,接收来自 device 设备的请求,转发给 server 服务器 + *

+ * 协议:HTTP + * + * @author haohao + */ +@Slf4j +public class IotDeviceUpstreamServer { + + private final Vertx vertx; + private final HttpServer server; + private final IotPluginEmqxProperties emqxProperties; + + public IotDeviceUpstreamServer(IotPluginCommonProperties commonProperties, + IotPluginEmqxProperties emqxProperties, + IotDeviceUpstreamApi deviceUpstreamApi, + IotDeviceDownstreamServer deviceDownstreamServer) { + this.emqxProperties = emqxProperties; + // 创建 Vertx 实例 + this.vertx = Vertx.vertx(); + // 创建 Router 实例 + Router router = Router.router(vertx); + router.route().handler(BodyHandler.create()); // 处理 Body + router.post(IotDeviceAuthVertxHandler.PATH) + .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); + // 创建 HttpServer 实例 + this.server = vertx.createHttpServer().requestHandler(router); + } + + /** + * 启动 HTTP 服务器 + */ + public void start() { + log.info("[start][开始启动]"); + server.listen(emqxProperties.getAuthPort()) + .toCompletionStage() + .toCompletableFuture() + .join(); + log.info("[start][启动完成,端口({})]", this.server.actualPort()); + } + + /** + * 停止所有 + */ + public void stop() { + log.info("[stop][开始关闭]"); + try { + // 关闭 HTTP 服务器 + if (server != null) { + server.close() + .toCompletionStage() + .toCompletableFuture() + .join(); + } + + // 关闭 Vertx 实例 + if (vertx != null) { + vertx.close() + .toCompletionStage() + .toCompletableFuture() + .join(); + } + log.info("[stop][关闭完成]"); + } catch (Exception e) { + log.error("[stop][关闭异常]", e); + throw new RuntimeException(e); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java new file mode 100644 index 0000000000..794949c287 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEmqxAuthReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * IoT Emqx 连接认证的 Vert.x Handler + * ... + * + * @author haohao + */ +@RequiredArgsConstructor +@Slf4j +public class IotDeviceAuthVertxHandler implements Handler { + + public static final String PATH = "/mqtt/auth"; + + private final IotDeviceUpstreamApi deviceUpstreamApi; + + @Override + @SuppressWarnings("unchecked") + public void handle(RoutingContext routingContext) { + + JsonObject json = routingContext.body().asJsonObject(); + String clientId = json.getString("clientid"); + String username = json.getString("username"); + String password = json.getString("password"); + + IotDeviceEmqxAuthReqDTO authReqDTO = buildDeviceEmqxAuthReqDTO(clientId, username, password); + + CommonResult authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); + if (authResult.getCode() != 0 || !authResult.getData()) { + denyAccess(routingContext); + return; + } + IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"allow\"}"); + } + + private void denyAccess(RoutingContext routingContext) { + IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"deny\"}"); + } + + private IotDeviceEmqxAuthReqDTO buildDeviceEmqxAuthReqDTO(String clientId, String username, String password) { + return new IotDeviceEmqxAuthReqDTO().setClientId(clientId).setUsername(username).setPassword(password); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml new file mode 100644 index 0000000000..f31880f24a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml @@ -0,0 +1,17 @@ +spring: + application: + name: yudao-module-iot-plugin-emqx + +yudao: + iot: + plugin: + common: + upstream-url: http://127.0.0.1:48080 + downstream-port: 8100 + plugin-key: yudao-module-iot-plugin-emqx + emqx: + host: 127.0.0.1 + port: 1883 + ssl: false + topics: "/sys/#" + auth-port: 8101 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java index 4e0a2ef448..bdb92b6ee7 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java @@ -21,7 +21,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; /** - * IoT 设备设备上报的 Vert.x Handler + * IoT 设备事件上报的 Vert.x Handler */ @RequiredArgsConstructor @Slf4j diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml index 4afeb4f26f..f195628a6a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/resources/application.yml @@ -8,5 +8,6 @@ yudao: common: upstream-url: http://127.0.0.1:48080 downstream-port: 8093 + plugin-key: yudao-module-iot-plugin-http http: server-port: 8092 From 8043ce612fd9e2429c7a1d67efbf630fbd98f5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Thu, 20 Feb 2025 18:31:34 +0800 Subject: [PATCH 186/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=B7=BB=E5=8A=A0=20IoT=20=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=20MQTT=20=E8=BF=9E=E6=8E=A5=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=93=8D=E5=BA=94=20VO=EF=BC=8C=E5=8C=85=E5=90=AB=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=20ID=E3=80=81=E7=94=A8=E6=88=B7=E5=90=8D?= =?UTF-8?q?=E5=92=8C=E5=AF=86=E7=A0=81=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotDeviceMqttConnectionParamsRespVO.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceMqttConnectionParamsRespVO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceMqttConnectionParamsRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceMqttConnectionParamsRespVO.java new file mode 100644 index 0000000000..5ce68c0fe1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceMqttConnectionParamsRespVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 设备 MQTT 连接参数 Response VO") +@Data +@ExcelIgnoreUnannotated +public class IotDeviceMqttConnectionParamsRespVO { + + @Schema(description = "MQTT 客户端 ID", example = "24602") + @ExcelProperty("MQTT 客户端 ID") + private String mqttClientId; + + @Schema(description = "MQTT 用户名", example = "芋艿") + @ExcelProperty("MQTT 用户名") + private String mqttUsername; + + @Schema(description = "MQTT 密码") + @ExcelProperty("MQTT 密码") + private String mqttPassword; + +} \ No newline at end of file From 22801802ac24cae2e2d5c7aecf8821952f7e1ce4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 21 Feb 2025 09:50:28 +0800 Subject: [PATCH 187/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E4=BD=BF=E7=94=A8=20OpenAiApi=20?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=E6=98=9F=E7=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/model/xinghuo/XingHuoChatModel.java | 138 ++---------------- .../ai/chat/XingHuoChatModelTests.java | 18 ++- 2 files changed, 25 insertions(+), 131 deletions(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatModel.java index 501d916db5..330d102a0f 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatModel.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatModel.java @@ -1,163 +1,45 @@ package cn.iocoder.yudao.framework.ai.core.model.xinghuo; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.lang.Assert; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.chat.metadata.ChatGenerationMetadata; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.ModelOptionsUtils; -import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.openai.api.OpenAiApi; -import org.springframework.ai.openai.metadata.OpenAiChatResponseMetadata; -import org.springframework.ai.retry.RetryUtils; -import org.springframework.http.ResponseEntity; -import org.springframework.retry.support.RetryTemplate; +import org.springframework.ai.openai.OpenAiChatModel; import reactor.core.publisher.Flux; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions.MODEL_DEFAULT; - /** * 讯飞星火 {@link ChatModel} 实现类 * * @author fansili */ @Slf4j +@RequiredArgsConstructor public class XingHuoChatModel implements ChatModel { - private static final String BASE_URL = "https://spark-api-open.xf-yun.com"; + public static final String BASE_URL = "https://spark-api-open.xf-yun.com"; - private final XingHuoChatOptions defaultOptions; - private final RetryTemplate retryTemplate; + public static final String MODEL_DEFAULT = "generalv3.5"; /** - * 星火兼容 OpenAI 的 HTTP 接口,所以复用它的实现,简化接入成本 - * - * 不过要注意,星火没有完全兼容,所以不能使用 {@link org.springframework.ai.openai.OpenAiChatModel} 调用,但是实现会参考它 + * 兼容 OpenAI 接口,进行复用 */ - private final OpenAiApi openAiApi; - - public XingHuoChatModel(String apiKey, String secretKey) { - this(apiKey, secretKey, - XingHuoChatOptions.builder().model(MODEL_DEFAULT).temperature(0.7F).build()); - } - - public XingHuoChatModel(String apiKey, String secretKey, XingHuoChatOptions options) { - this(apiKey, secretKey, options, RetryUtils.DEFAULT_RETRY_TEMPLATE); - } - - public XingHuoChatModel(String apiKey, String secretKey, XingHuoChatOptions options, RetryTemplate retryTemplate) { - Assert.notEmpty(apiKey, "apiKey 不能为空"); - Assert.notEmpty(secretKey, "secretKey 不能为空"); - Assert.notNull(options, "options 不能为空"); - Assert.notNull(retryTemplate, "retryTemplate 不能为空"); - this.openAiApi = new OpenAiApi(BASE_URL, apiKey + ":" + secretKey); - this.defaultOptions = options; - this.retryTemplate = retryTemplate; - } + private final OpenAiChatModel openAiChatModel; @Override public ChatResponse call(Prompt prompt) { - OpenAiApi.ChatCompletionRequest request = createRequest(prompt, false); - return this.retryTemplate.execute(ctx -> { - // 1.1 发起调用 - ResponseEntity completionEntity = openAiApi.chatCompletionEntity(request); - // 1.2 校验结果 - OpenAiApi.ChatCompletion chatCompletion = completionEntity.getBody(); - if (chatCompletion == null) { - log.warn("No chat completion returned for prompt: {}", prompt); - return new ChatResponse(ListUtil.of()); - } - List choices = chatCompletion.choices(); - if (choices == null) { - log.warn("No choices returned for prompt: {}", prompt); - return new ChatResponse(ListUtil.of()); - } - - // 2. 转换 ChatResponse 返回 - List generations = choices.stream().map(choice -> { - Generation generation = new Generation(choice.message().content(), toMap(chatCompletion.id(), choice)); - if (choice.finishReason() != null) { - generation.withGenerationMetadata(ChatGenerationMetadata.from(choice.finishReason().name(), null)); - } - return generation; - }).toList(); - return new ChatResponse(generations, - OpenAiChatResponseMetadata.from(completionEntity.getBody())); - }); - } - - private Map toMap(String id, OpenAiApi.ChatCompletion.Choice choice) { - Map map = new HashMap<>(); - OpenAiApi.ChatCompletionMessage message = choice.message(); - if (message.role() != null) { - map.put("role", message.role().name()); - } - if (choice.finishReason() != null) { - map.put("finishReason", choice.finishReason().name()); - } - map.put("id", id); - return map; + return openAiChatModel.call(prompt); } @Override public Flux stream(Prompt prompt) { - OpenAiApi.ChatCompletionRequest request = createRequest(prompt, true); - return this.retryTemplate.execute(ctx -> { - // 1. 发起调用 - Flux response = this.openAiApi.chatCompletionStream(request); - return response.map(chatCompletion -> { - String id = chatCompletion.id(); - // 2. 转换 ChatResponse 返回 - List generations = chatCompletion.choices().stream().map(choice -> { - String finish = (choice.finishReason() != null ? choice.finishReason().name() : ""); - Generation generation = new Generation(choice.delta().content(), - Map.of("id", id, "role", choice.delta().role().name(), "finishReason", finish)); - if (choice.finishReason() != null) { - generation = generation.withGenerationMetadata( - ChatGenerationMetadata.from(choice.finishReason().name(), null)); - } - return generation; - }).toList(); - return new ChatResponse(generations); - }); - }); - } - - OpenAiApi.ChatCompletionRequest createRequest(Prompt prompt, boolean stream) { - // 1. 构建 ChatCompletionMessage 对象 - List chatCompletionMessages = prompt.getInstructions().stream().map(m -> - new OpenAiApi.ChatCompletionMessage(m.getContent(), OpenAiApi.ChatCompletionMessage.Role.valueOf(m.getMessageType().name()))).toList(); - OpenAiApi.ChatCompletionRequest request = new OpenAiApi.ChatCompletionRequest(chatCompletionMessages, stream); - - // 2.1 补充 prompt 内置的 options - if (prompt.getOptions() != null) { - if (prompt.getOptions() instanceof ChatOptions runtimeOptions) { - OpenAiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions, - ChatOptions.class, OpenAiChatOptions.class); - request = ModelOptionsUtils.merge(updatedRuntimeOptions, request, OpenAiApi.ChatCompletionRequest.class); - } else { - throw new IllegalArgumentException("Prompt options are not of type ChatOptions: " - + prompt.getOptions().getClass().getSimpleName()); - } - } - // 2.2 补充默认 options - if (this.defaultOptions != null) { - request = ModelOptionsUtils.merge(request, this.defaultOptions, OpenAiApi.ChatCompletionRequest.class); - } - return request; + return openAiChatModel.stream(prompt); } @Override public ChatOptions getDefaultOptions() { - return XingHuoChatOptions.fromOptions(defaultOptions); + return openAiChatModel.getDefaultOptions(); } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java index 63f76b96d1..4140b4bfbd 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java @@ -8,6 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -20,9 +23,18 @@ import java.util.List; */ public class XingHuoChatModelTests { - private final XingHuoChatModel chatModel = new XingHuoChatModel( - "cb6415c19d6162cda07b47316fcb0416", - "Y2JiYTIxZjA3MDMxMjNjZjQzYzVmNzdh"); + private static final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(XingHuoChatModel.BASE_URL) + .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model("generalv3.5") // 模型 + .temperature(0.7) + .build()) + .build(); + + private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel); @Test @Disabled From f582c9cfa3901921d3b6f75a85ab17bf42cb9fde Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 21 Feb 2025 13:46:54 +0800 Subject: [PATCH 188/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E4=BD=BF=E7=94=A8=20OpenAiApi=20?= =?UTF-8?q?=E6=8E=A5=E5=85=A5=20deepseek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/deepseek/DeepSeekChatModel.java | 141 ++---------------- .../model/deepseek/DeepSeekChatOptions.java | 55 ------- .../model/xinghuo/XingHuoChatOptions.java | 55 ------- .../ai/chat/DeepSeekChatModelTests.java | 16 +- 4 files changed, 25 insertions(+), 242 deletions(-) delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatOptions.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatOptions.java diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatModel.java index e3097b83a3..a136b5a2b5 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatModel.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatModel.java @@ -1,166 +1,45 @@ package cn.iocoder.yudao.framework.ai.core.model.deepseek; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.lang.Assert; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.chat.metadata.ChatGenerationMetadata; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.model.Generation; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.model.ModelOptionsUtils; -import org.springframework.ai.openai.OpenAiChatOptions; -import org.springframework.ai.openai.api.OpenAiApi; -import org.springframework.ai.openai.metadata.OpenAiChatResponseMetadata; -import org.springframework.ai.retry.RetryUtils; -import org.springframework.http.ResponseEntity; -import org.springframework.retry.support.RetryTemplate; +import org.springframework.ai.openai.OpenAiChatModel; import reactor.core.publisher.Flux; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions.MODEL_DEFAULT; - /** * DeepSeek {@link ChatModel} 实现类 * * @author fansili */ @Slf4j +@RequiredArgsConstructor public class DeepSeekChatModel implements ChatModel { - private static final String BASE_URL = "https://api.deepseek.com"; + public static final String BASE_URL = "https://api.deepseek.com"; - private final DeepSeekChatOptions defaultOptions; - private final RetryTemplate retryTemplate; + public static final String MODEL_DEFAULT = "deepseek-chat"; /** - * DeepSeek 兼容 OpenAI 的 HTTP 接口,所以复用它的实现,简化接入成本 - * - * 不过要注意,DeepSeek 没有完全兼容,所以不能使用 {@link org.springframework.ai.openai.OpenAiChatModel} 调用,但是实现会参考它 + * 兼容 OpenAI 接口,进行复用 */ - private final OpenAiApi openAiApi; - - public DeepSeekChatModel(String apiKey) { - this(apiKey, DeepSeekChatOptions.builder().model(MODEL_DEFAULT).temperature(0.7F).build()); - } - - public DeepSeekChatModel(String apiKey, DeepSeekChatOptions options) { - this(apiKey, options, RetryUtils.DEFAULT_RETRY_TEMPLATE); - } - - public DeepSeekChatModel(String apiKey, DeepSeekChatOptions options, RetryTemplate retryTemplate) { - Assert.notEmpty(apiKey, "apiKey 不能为空"); - Assert.notNull(options, "options 不能为空"); - Assert.notNull(retryTemplate, "retryTemplate 不能为空"); - this.openAiApi = new OpenAiApi(BASE_URL, apiKey); - this.defaultOptions = options; - this.retryTemplate = retryTemplate; - } + private final OpenAiChatModel openAiChatModel; @Override public ChatResponse call(Prompt prompt) { - OpenAiApi.ChatCompletionRequest request = createRequest(prompt, false); - return this.retryTemplate.execute(ctx -> { - // 1.1 发起调用 - ResponseEntity completionEntity = openAiApi.chatCompletionEntity(request); - // 1.2 校验结果 - OpenAiApi.ChatCompletion chatCompletion = completionEntity.getBody(); - if (chatCompletion == null) { - log.warn("No chat completion returned for prompt: {}", prompt); - return new ChatResponse(ListUtil.of()); - } - List choices = chatCompletion.choices(); - if (choices == null) { - log.warn("No choices returned for prompt: {}", prompt); - return new ChatResponse(ListUtil.of()); - } - - // 2. 转换 ChatResponse 返回 - List generations = choices.stream().map(choice -> { - Generation generation = new Generation(choice.message().content(), toMap(chatCompletion.id(), choice)); - if (choice.finishReason() != null) { - generation.withGenerationMetadata(ChatGenerationMetadata.from(choice.finishReason().name(), null)); - } - return generation; - }).toList(); - return new ChatResponse(generations, - OpenAiChatResponseMetadata.from(completionEntity.getBody())); - }); - } - - private Map toMap(String id, OpenAiApi.ChatCompletion.Choice choice) { - Map map = new HashMap<>(); - OpenAiApi.ChatCompletionMessage message = choice.message(); - if (message.role() != null) { - map.put("role", message.role().name()); - } - if (choice.finishReason() != null) { - map.put("finishReason", choice.finishReason().name()); - } - map.put("id", id); - return map; + return openAiChatModel.call(prompt); } @Override public Flux stream(Prompt prompt) { - OpenAiApi.ChatCompletionRequest request = createRequest(prompt, true); - return this.retryTemplate.execute(ctx -> { - // 1. 发起调用 - Flux response = this.openAiApi.chatCompletionStream(request); - return response.map(chatCompletion -> { - String id = chatCompletion.id(); - // 2. 转换 ChatResponse 返回 - List generations = chatCompletion.choices().stream().map(choice -> { - String finish = (choice.finishReason() != null ? choice.finishReason().name() : ""); - String role = (choice.delta().role() != null ? choice.delta().role().name() : ""); - if (choice.finishReason() == OpenAiApi.ChatCompletionFinishReason.STOP) { - // 兜底处理 DeepSeek 返回 STOP 时,role 为空的情况 - role = OpenAiApi.ChatCompletionMessage.Role.ASSISTANT.name(); - } - Generation generation = new Generation(choice.delta().content(), - Map.of("id", id, "role", role, "finishReason", finish)); - if (choice.finishReason() != null) { - generation = generation.withGenerationMetadata( - ChatGenerationMetadata.from(choice.finishReason().name(), null)); - } - return generation; - }).toList(); - return new ChatResponse(generations); - }); - }); - } - - OpenAiApi.ChatCompletionRequest createRequest(Prompt prompt, boolean stream) { - // 1. 构建 ChatCompletionMessage 对象 - List chatCompletionMessages = prompt.getInstructions().stream().map(m -> - new OpenAiApi.ChatCompletionMessage(m.getContent(), OpenAiApi.ChatCompletionMessage.Role.valueOf(m.getMessageType().name()))).toList(); - OpenAiApi.ChatCompletionRequest request = new OpenAiApi.ChatCompletionRequest(chatCompletionMessages, stream); - - // 2.1 补充 prompt 内置的 options - if (prompt.getOptions() != null) { - if (prompt.getOptions() instanceof ChatOptions runtimeOptions) { - OpenAiChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(runtimeOptions, - ChatOptions.class, OpenAiChatOptions.class); - request = ModelOptionsUtils.merge(updatedRuntimeOptions, request, OpenAiApi.ChatCompletionRequest.class); - } else { - throw new IllegalArgumentException("Prompt options are not of type ChatOptions: " - + prompt.getOptions().getClass().getSimpleName()); - } - } - // 2.2 补充默认 options - if (this.defaultOptions != null) { - request = ModelOptionsUtils.merge(request, this.defaultOptions, OpenAiApi.ChatCompletionRequest.class); - } - return request; + return openAiChatModel.stream(prompt); } @Override public ChatOptions getDefaultOptions() { - return DeepSeekChatOptions.fromOptions(defaultOptions); + return openAiChatModel.getDefaultOptions(); } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatOptions.java deleted file mode 100644 index e07e3f0865..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/deepseek/DeepSeekChatOptions.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.framework.ai.core.model.deepseek; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.ai.chat.prompt.ChatOptions; - -/** - * DeepSeek {@link ChatOptions} 实现类 - * - * 参考文档:快速开始 - * - * @author fansili - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class DeepSeekChatOptions implements ChatOptions { - - public static final String MODEL_DEFAULT = "deepseek-chat"; - - /** - * 模型 - */ - private String model; - /** - * 温度 - */ - private Float temperature; - /** - * 最大 Token - */ - private Integer maxTokens; - /** - * topP - */ - private Float topP; - - @Override - public Integer getTopK() { - return null; - } - - public static DeepSeekChatOptions fromOptions(DeepSeekChatOptions fromOptions) { - return DeepSeekChatOptions.builder() - .model(fromOptions.getModel()) - .temperature(fromOptions.getTemperature()) - .maxTokens(fromOptions.getMaxTokens()) - .topP(fromOptions.getTopP()) - .build(); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatOptions.java deleted file mode 100644 index e3287b613a..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/xinghuo/XingHuoChatOptions.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.framework.ai.core.model.xinghuo; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.ai.chat.prompt.ChatOptions; - -/** - * 讯飞星火 {@link ChatOptions} 实现类 - * - * 参考文档:HTTP 调用 - * - * @author fansili - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class XingHuoChatOptions implements ChatOptions { - - public static final String MODEL_DEFAULT = "generalv3.5"; - - /** - * 模型 - */ - private String model; - /** - * 温度 - */ - private Float temperature; - /** - * 最大 Token - */ - private Integer maxTokens; - /** - * K 个候选 - */ - private Integer topK; - - @Override - public Float getTopP() { - return null; - } - - public static XingHuoChatOptions fromOptions(XingHuoChatOptions fromOptions) { - return XingHuoChatOptions.builder() - .model(fromOptions.getModel()) - .temperature(fromOptions.getTemperature()) - .maxTokens(fromOptions.getMaxTokens()) - .topK(fromOptions.getTopK()) - .build(); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DeepSeekChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DeepSeekChatModelTests.java index f66c548176..b3e12bfd16 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DeepSeekChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DeepSeekChatModelTests.java @@ -8,6 +8,9 @@ import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.ai.openai.api.OpenAiApi; import reactor.core.publisher.Flux; import java.util.ArrayList; @@ -20,7 +23,18 @@ import java.util.List; */ public class DeepSeekChatModelTests { - private final DeepSeekChatModel chatModel = new DeepSeekChatModel("sk-e94db327cc7d457d99a8de8810fc6b12"); + private static final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(DeepSeekChatModel.BASE_URL) + .apiKey("sk-e52047409b144d97b791a6a46a2d") // apiKey + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model("deepseek-chat") // 模型 + .temperature(0.7) + .build()) + .build(); + + private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel); @Test @Disabled From 3b7b81829d4d31d6387068eb48fe01123a94245b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 09:22:58 +0800 Subject: [PATCH 189/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E4=BD=BF=E7=94=A8=20alibaba=20ai?= =?UTF-8?q?=20=E6=9B=BF=E4=BB=A3=20tongyi=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/chat/TongYiChatModelTests.java | 28 +++++-------------- .../ai/chat/XingHuoChatModelTests.java | 22 +++++++-------- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java index 00bc2b9001..aa521373c4 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java @@ -1,12 +1,8 @@ package cn.iocoder.yudao.framework.ai.chat; -import cn.hutool.core.util.ReflectUtil; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatOptions; -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.common.MessageManager; -import com.alibaba.dashscope.utils.Constants; -import org.junit.jupiter.api.BeforeEach; +import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.chat.messages.Message; @@ -20,25 +16,15 @@ import java.util.ArrayList; import java.util.List; /** - * {@link TongYiChatModel} 集成测试类 + * {@link DashScopeChatModel} 集成测试类 * * @author fansili */ public class TongYiChatModelTests { - private final Generation generation = new Generation(); - private final TongYiChatModel chatModel = new TongYiChatModel(generation, - TongYiChatOptions.builder().withModel("qwen1.5-72b-chat").build()); - - static { - Constants.apiKey = "sk-Zsd81gZYg7"; - } - - @BeforeEach - public void before() { - // 防止 TongYiChatModel 调用空指针 - ReflectUtil.setFieldValue(chatModel, "msgManager", new MessageManager()); - } + private final DashScopeChatModel chatModel = new DashScopeChatModel( + new DashScopeApi("sk-7d903764249848cfa912733146da12d1"), + DashScopeChatOptions.builder().withModel("qwen1.5-72b-chat").build()); @Test @Disabled diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java index 4140b4bfbd..cbc7f6e166 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/XingHuoChatModelTests.java @@ -23,18 +23,18 @@ import java.util.List; */ public class XingHuoChatModelTests { - private static final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() - .openAiApi(OpenAiApi.builder() - .baseUrl(XingHuoChatModel.BASE_URL) - .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey - .build()) - .defaultOptions(OpenAiChatOptions.builder() - .model("generalv3.5") // 模型 - .temperature(0.7) - .build()) - .build(); + private static final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(XingHuoChatModel.BASE_URL) + .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model("generalv3.5") // 模型 + .temperature(0.7) + .build()) + .build(); - private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel); + private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel); @Test @Disabled From 7ef73b7d0992486c380c49e7f214f48ebf0f29d4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 10:47:18 +0800 Subject: [PATCH 190/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9Aspring=20ai=20=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=EF=BC=8C=E5=8D=87=E7=BA=A7=E5=88=B0=201.0.0-M6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/framework/web/package-info.java | 4 +- .../ai/service/image/AiImageServiceImpl.java | 3 +- .../yudao-spring-boot-starter-ai/pom.xml | 9 +- .../ai/config/YudaoAiAutoConfiguration.java | 59 ++- .../ai/config/YudaoAiProperties.java | 8 +- .../ai/core/factory/AiModelFactoryImpl.java | 144 +++--- .../model/midjourney/api/MidjourneyApi.java | 14 +- .../yudao/framework/ai/core/util/AiUtils.java | 28 +- .../RedisVectorStoreAutoConfiguration.java | 61 --- .../ai/vectorstore/RedisVectorStore.java | 456 ------------------ .../ai/chat/AzureOpenAIChatModelTests.java | 9 +- .../ai/chat/LlamaChatModelTests.java | 86 ++-- .../ai/chat/OpenAIChatModelTests.java | 2 +- .../ai/chat/YiYanChatModelTests.java | 2 +- .../ai/chat/ZhiPuAiChatModelTests.java | 2 +- .../framework/ai/image/QianFanImageTests.java | 6 +- .../ai/image/TongYiImagesModelTest.java | 18 +- .../ai/image/ZhiPuAiImageModelTests.java | 2 +- 18 files changed, 199 insertions(+), 714 deletions(-) delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java index 09de7263c5..e979056d4e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java @@ -1,4 +1,4 @@ /** - * crm 模块的 web 拓展封装 + * ai 模块的 web 拓展封装 */ -package cn.iocoder.yudao.module.crm.framework.web; +package cn.iocoder.yudao.module.ai.framework.web; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java index e8532a5762..3e8c09f079 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java @@ -20,7 +20,6 @@ import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; import cn.iocoder.yudao.module.infra.api.file.FileApi; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesOptions; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.image.ImageModel; @@ -144,7 +143,7 @@ public class AiImageServiceImpl implements AiImageService { .withClipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset"))) .build(); } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) { - return TongYiImagesOptions.builder() + return DashScopeImageOptions.builder() .withModel(draw.getModel()).withN(1) .withHeight(draw.getHeight()).withWidth(draw.getWidth()) .build(); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index a270f76558..156d3f0769 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -14,8 +14,8 @@ ${project.artifactId} AI 大模型拓展,接入国内外大模型 - group.springframework.ai - 1.1.0 + org.springframework.ai + 1.0.0-M6 @@ -90,6 +90,11 @@ spring-ai-qianfan-spring-boot-starter ${spring-ai.version} + + com.alibaba.cloud.ai + spring-ai-alibaba-starter + 1.0.0-M5.1 + diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 0d2620b0cb..3aed3ee1b9 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -1,15 +1,16 @@ package cn.iocoder.yudao.framework.ai.config; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; -import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; -import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions; -import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; import org.springframework.ai.tokenizer.TokenCountEstimator; import org.springframework.ai.transformer.splitter.TokenTextSplitter; @@ -17,7 +18,6 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Lazy; /** @@ -28,7 +28,6 @@ import org.springframework.context.annotation.Lazy; @AutoConfiguration @EnableConfigurationProperties(YudaoAiProperties.class) @Slf4j -@Import(TongYiAutoConfiguration.class) public class YudaoAiAutoConfiguration { @Bean @@ -43,26 +42,52 @@ public class YudaoAiAutoConfiguration { @ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true") public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) { YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepSeek(); - DeepSeekChatOptions options = DeepSeekChatOptions.builder() - .model(properties.getModel()) - .temperature(properties.getTemperature()) - .maxTokens(properties.getMaxTokens()) - .topP(properties.getTopP()) + return buildDeepSeekChatModel(properties); + } + + public DeepSeekChatModel buildDeepSeekChatModel(YudaoAiProperties.DeepSeekProperties properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(DeepSeekChatModel.MODEL_DEFAULT); + } + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(DeepSeekChatModel.BASE_URL) + .apiKey(properties.getApiKey()) + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model(properties.getModel()) + .temperature(properties.getTemperature()) + .maxTokens(properties.getMaxTokens()) + .topP(properties.getTopP()) + .build()) .build(); - return new DeepSeekChatModel(properties.getApiKey(), options); + return new DeepSeekChatModel(openAiChatModel); } @Bean @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true") public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) { YudaoAiProperties.XingHuoProperties properties = yudaoAiProperties.getXinghuo(); - XingHuoChatOptions options = XingHuoChatOptions.builder() - .model(properties.getModel()) - .temperature(properties.getTemperature()) - .maxTokens(properties.getMaxTokens()) - .topK(properties.getTopK()) + return buildXingHuoChatClient(properties); + } + + public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuoProperties properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(XingHuoChatModel.MODEL_DEFAULT); + } + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(XingHuoChatModel.BASE_URL) + .apiKey(properties.getAppKey() + ":" + properties.getSecretKey()) + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model(properties.getModel()) + .temperature(properties.getTemperature()) + .maxTokens(properties.getMaxTokens()) + .topP(properties.getTopP()) + .build()) .build(); - return new XingHuoChatModel(properties.getAppKey(), properties.getSecretKey(), options); + return new XingHuoChatModel(openAiChatModel); } @Bean diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java index 82c74b0c64..7a98c4376a 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java @@ -42,9 +42,9 @@ public class YudaoAiProperties { private String secretKey; private String model; - private Float temperature; + private Double temperature; private Integer maxTokens; - private Integer topK; + private Double topP; } @@ -55,9 +55,9 @@ public class YudaoAiProperties { private String apiKey; private String model; - private Float temperature; + private Double temperature; private Integer maxTokens; - private Float topP; + private Double topP; } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 7acd247691..985d8fd041 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -13,60 +13,45 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; -import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; -import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration; -import com.alibaba.cloud.ai.tongyi.TongYiConnectionProperties; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatProperties; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties; -import com.alibaba.dashscope.aigc.generation.Generation; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.embeddings.TextEmbedding; -import com.azure.ai.openai.OpenAIClient; +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; +import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; +import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; +import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; +import com.azure.ai.openai.OpenAIClientBuilder; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties; import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; -import org.springframework.ai.autoconfigure.qianfan.QianFanChatProperties; -import org.springframework.ai.autoconfigure.qianfan.QianFanConnectionProperties; -import org.springframework.ai.autoconfigure.qianfan.QianFanImageProperties; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; -import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiChatProperties; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; -import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; -import org.springframework.ai.model.function.FunctionCallbackContext; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiImageModel; -import org.springframework.ai.openai.api.ApiUtils; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiImageApi; +import org.springframework.ai.openai.api.common.OpenAiApiConstants; import org.springframework.ai.qianfan.QianFanChatModel; import org.springframework.ai.qianfan.QianFanImageModel; import org.springframework.ai.qianfan.api.QianFanApi; import org.springframework.ai.qianfan.api.QianFanImageApi; import org.springframework.ai.stabilityai.StabilityAiImageModel; import org.springframework.ai.stabilityai.api.StabilityAiApi; -import org.springframework.ai.vectorstore.RedisVectorStore; +import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.zhipuai.ZhiPuAiChatModel; import org.springframework.ai.zhipuai.ZhiPuAiImageModel; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; -import org.springframework.boot.autoconfigure.data.redis.RedisProperties; -import org.springframework.retry.support.RetryTemplate; -import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestClient; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.search.Schema; import java.util.List; @@ -110,7 +95,7 @@ public class AiModelFactoryImpl implements AiModelFactory { //noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: - return SpringUtil.getBean(TongYiChatModel.class); + return SpringUtil.getBean(DashScopeChatModel.class); case YI_YAN: return SpringUtil.getBean(QianFanChatModel.class); case DEEP_SEEK: @@ -135,7 +120,7 @@ public class AiModelFactoryImpl implements AiModelFactory { //noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: - return SpringUtil.getBean(TongYiImagesModel.class); + return SpringUtil.getBean(DashScopeImageModel.class); case YI_YAN: return SpringUtil.getBean(QianFanImageModel.class); case ZHI_PU: @@ -202,17 +187,20 @@ public class AiModelFactoryImpl implements AiModelFactory { String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url); return Singleton.get(cacheKey, (Func0) () -> { String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey); - var config = RedisVectorStore.RedisVectorStoreConfig.builder() - .withIndexName(cacheKey) - .withPrefix(prefix) - .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC)) - .build(); - RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); - RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, - new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), - true); - redisVectorStore.afterPropertiesSet(); - return redisVectorStore; + // TODO @芋艿:先临时使用 store + return SimpleVectorStore.builder(embeddingModel).build(); + // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的 +// var config = RedisVectorStore.RedisVectorStoreConfig.builder() +// .withIndexName(cacheKey) +// .withPrefix(prefix) +// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC)) +// .build(); +// RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); +// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, +// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), +// true); +// redisVectorStore.afterPropertiesSet(); +// return redisVectorStore; }); } @@ -226,29 +214,23 @@ public class AiModelFactoryImpl implements AiModelFactory { // ========== 各种创建 spring-ai 客户端的方法 ========== /** - * 可参考 {@link TongYiAutoConfiguration#tongYiChatClient(Generation, TongYiChatProperties, TongYiConnectionProperties)} + * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeChatModel 方法 */ - private static TongYiChatModel buildTongYiChatModel(String key) { - com.alibaba.dashscope.aigc.generation.Generation generation = SpringUtil.getBean(Generation.class); - TongYiChatProperties chatOptions = SpringUtil.getBean(TongYiChatProperties.class); - // TODO @芋艿:貌似 apiKey 是全局唯一的???得测试下 - // TODO @芋艿:貌似阿里云不是增量返回的 - // 该 issue 进行跟进中 https://github.com/alibaba/spring-cloud-alibaba/issues/3790 - TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties(); - connectionProperties.setApiKey(key); - return new TongYiAutoConfiguration().tongYiChatClient(generation, chatOptions, connectionProperties); - } - - private static TongYiImagesModel buildTongYiImagesModel(String key) { - ImageSynthesis imageSynthesis = SpringUtil.getBean(ImageSynthesis.class); - TongYiImagesProperties imagesOptions = SpringUtil.getBean(TongYiImagesProperties.class); - TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties(); - connectionProperties.setApiKey(key); - return new TongYiAutoConfiguration().tongYiImagesClient(imageSynthesis, imagesOptions, connectionProperties); + private static DashScopeChatModel buildTongYiChatModel(String key) { + DashScopeApi dashScopeApi = new DashScopeApi(key); + return new DashScopeChatModel(dashScopeApi); } /** - * 可参考 {@link QianFanAutoConfiguration#qianFanChatModel(QianFanConnectionProperties, QianFanChatProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} + * 可参考 {@link DashScopeAutoConfiguration} 的 dashScopeImageModel 方法 + */ + private static DashScopeImageModel buildTongYiImagesModel(String key) { + DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key); + return new DashScopeImageModel(dashScopeImageApi); + } + + /** + * 可参考 {@link QianFanAutoConfiguration} 的 qianFanChatModel 方法 */ private static QianFanChatModel buildYiYanChatModel(String key) { List keys = StrUtil.split(key, '|'); @@ -260,7 +242,7 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link QianFanAutoConfiguration#qianFanImageModel(QianFanConnectionProperties, QianFanImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} + * 可参考 {@link QianFanAutoConfiguration} 的 qianFanImageModel 方法 */ private QianFanImageModel buildQianFanImageModel(String key) { List keys = StrUtil.split(key, '|'); @@ -275,11 +257,13 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link YudaoAiAutoConfiguration#deepSeekChatModel(YudaoAiProperties)} */ private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) { - return new DeepSeekChatModel(apiKey); + YudaoAiProperties.DeepSeekProperties properties = new YudaoAiProperties.DeepSeekProperties() + .setApiKey(apiKey); + return new YudaoAiAutoConfiguration().buildDeepSeekChatModel(properties); } /** - * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)} + * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiChatModel 方法 */ private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); @@ -288,7 +272,7 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)} + * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiImageModel 方法 */ private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); @@ -301,21 +285,22 @@ public class AiModelFactoryImpl implements AiModelFactory { */ private static XingHuoChatModel buildXingHuoChatModel(String key) { List keys = StrUtil.split(key, '|'); - Assert.equals(keys.size(), 3, "XingHuoChatClient 的密钥需要 (appid|appKey|secretKey) 格式"); - String appKey = keys.get(1); - String secretKey = keys.get(2); - return new XingHuoChatModel(appKey, secretKey); + Assert.equals(keys.size(), 2, "XingHuoChatClient 的密钥需要 (appKey|secretKey) 格式"); + YudaoAiProperties.XingHuoProperties properties = new YudaoAiProperties.XingHuoProperties() + .setAppKey(keys.get(0)).setSecretKey(keys.get(1)); + return new YudaoAiAutoConfiguration().buildXingHuoChatClient(properties); } /** - * 可参考 {@link OpenAiAutoConfiguration} + * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法 */ private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { - url = StrUtil.blankToDefault(url, ApiUtils.DEFAULT_BASE_URL); - OpenAiApi openAiApi = new OpenAiApi(url, openAiToken); - return new OpenAiChatModel(openAiApi); + url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); + OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); + return OpenAiChatModel.builder().openAiApi(openAiApi).build(); } + // TODO @芋艿:手头暂时没密钥,使用建议再测试下 /** * 可参考 {@link AzureOpenAiAutoConfiguration} */ @@ -325,27 +310,28 @@ public class AiModelFactoryImpl implements AiModelFactory { AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties(); connectionProperties.setApiKey(apiKey); connectionProperties.setEndpoint(url); - OpenAIClient openAIClient = azureOpenAiAutoConfiguration.openAIClient(connectionProperties); + OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null); // 获取 AzureOpenAiChatProperties 对象 AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); - return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, null, null); + return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, + null, null, null); } /** - * 可参考 {@link OpenAiAutoConfiguration} + * 可参考 {@link OpenAiAutoConfiguration} 的 openAiImageModel 方法 */ private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { - url = StrUtil.blankToDefault(url, ApiUtils.DEFAULT_BASE_URL); - OpenAiImageApi openAiApi = new OpenAiImageApi(url, openAiToken, RestClient.builder()); + url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); + OpenAiImageApi openAiApi = OpenAiImageApi.builder().baseUrl(url).apiKey(openAiToken).build(); return new OpenAiImageModel(openAiApi); } /** - * 可参考 {@link OllamaAutoConfiguration} + * 可参考 {@link OllamaAutoConfiguration} 的 ollamaApi 方法 */ private static OllamaChatModel buildOllamaChatModel(String url) { OllamaApi ollamaApi = new OllamaApi(url); - return new OllamaChatModel(ollamaApi); + return OllamaChatModel.builder().ollamaApi(ollamaApi).build(); } private StabilityAiImageModel buildStabilityAiImageModel(String apiKey, String url) { @@ -356,13 +342,13 @@ public class AiModelFactoryImpl implements AiModelFactory { // ========== 各种创建 EmbeddingModel 的方法 ========== + // TODO @芋艿:需要测试下 /** - * 可参考 {@link TongYiAutoConfiguration#tongYiTextEmbeddingClient(TextEmbedding, TongYiConnectionProperties)} + * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeEmbeddingModel 方法 */ private EmbeddingModel buildTongYiEmbeddingModel(String apiKey) { - TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties(); - connectionProperties.setApiKey(apiKey); - return new TongYiAutoConfiguration().tongYiTextEmbeddingClient(SpringUtil.getBean(TextEmbedding.class), connectionProperties); + DashScopeApi dashScopeApi = new DashScopeApi(apiKey); + return new DashScopeEmbeddingModel(dashScopeApi); } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/midjourney/api/MidjourneyApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/midjourney/api/MidjourneyApi.java index 55091c78d4..a03a0c844f 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/midjourney/api/MidjourneyApi.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/midjourney/api/MidjourneyApi.java @@ -8,9 +8,10 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.openai.api.ApiUtils; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatusCode; +import org.springframework.http.MediaType; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @@ -18,6 +19,7 @@ import reactor.core.publisher.Mono; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -50,11 +52,19 @@ public class MidjourneyApi { public MidjourneyApi(String baseUrl, String apiKey, String notifyUrl) { this.webClient = WebClient.builder() .baseUrl(baseUrl) - .defaultHeaders(ApiUtils.getJsonContentHeaders(apiKey)) + .defaultHeaders(getJsonContentHeaders(apiKey)) .build(); this.notifyUrl = notifyUrl; } + // TODO @芋艿:这里,看看怎么调整下???https://github.com/spring-projects/spring-ai/issues/741 + public static Consumer getJsonContentHeaders(String apiKey) { + return (headers) -> { + headers.setBearerAuth(apiKey); + headers.setContentType(MediaType.APPLICATION_JSON); + }; + }; + /** * imagine - 根据提示词提交绘画任务 * diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index e18f100156..7203db4593 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -2,9 +2,7 @@ package cn.iocoder.yudao.framework.ai.core.util; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; -import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions; -import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions; -import com.alibaba.cloud.ai.tongyi.chat.TongYiChatOptions; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.prompt.ChatOptions; @@ -21,26 +19,24 @@ import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; public class AiUtils { public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { - Float temperatureF = temperature != null ? temperature.floatValue() : null; //noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: - return TongYiChatOptions.builder().withModel(model).withTemperature(temperature).withMaxTokens(maxTokens).build(); + // TODO @芋艿:tongyi 暂时没 maxTokens 选项 + return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).build(); case YI_YAN: - return QianFanChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build(); - case DEEP_SEEK: - return DeepSeekChatOptions.builder().model(model).temperature(temperatureF).maxTokens(maxTokens).build(); + return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: - return ZhiPuAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build(); - case XING_HUO: - return XingHuoChatOptions.builder().model(model).temperature(temperatureF).maxTokens(maxTokens).build(); + return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case OPENAI: - return OpenAiChatOptions.builder().withModel(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build(); + case DEEP_SEEK: // 复用 OpenAI 客户端 + case XING_HUO: // 复用 OpenAI 客户端 + return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case AZURE_OPENAI: // TODO 芋艿:貌似没 model 字段???! - return AzureOpenAiChatOptions.builder().withDeploymentName(model).withTemperature(temperatureF).withMaxTokens(maxTokens).build(); + return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens).build(); case OLLAMA: - return OllamaOptions.create().withModel(model).withTemperature(temperatureF).withNumPredict(maxTokens); + return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens).build(); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -56,8 +52,8 @@ public class AiUtils { if (MessageType.SYSTEM.getValue().equals(type)) { return new SystemMessage(content); } - if (MessageType.FUNCTION.getValue().equals(type)) { - return new FunctionMessage(content); + if (MessageType.TOOL.getValue().equals(type)) { + throw new UnsupportedOperationException("暂不支持 tool 消息:" + content); } throw new IllegalArgumentException(StrUtil.format("未知消息类型({})", type)); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java deleted file mode 100644 index a72d50c4a8..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2023 - 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.ai.autoconfigure.vectorstore.redis; - -import org.springframework.ai.embedding.EmbeddingModel; -import org.springframework.ai.vectorstore.RedisVectorStore; -import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import redis.clients.jedis.JedisPooled; - -/** - * TODO @xin 先拿 spring-ai 最新代码覆盖,1.0.0-M1 跟 redis 自动配置会冲突 - * - * TODO 这个官方,有说啥时候 fix 哇? - * TODO 看着是列在1.0.0-M2版本 - * - * @author Christian Tzolov - * @author Eddú Meléndez - */ -@AutoConfiguration(after = RedisAutoConfiguration.class) -@ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class}) -@ConditionalOnBean(JedisConnectionFactory.class) -@EnableConfigurationProperties(RedisVectorStoreProperties.class) -public class RedisVectorStoreAutoConfiguration { - - @Bean - @ConditionalOnMissingBean - public RedisVectorStore vectorStore(EmbeddingModel embeddingModel, RedisVectorStoreProperties properties, - JedisConnectionFactory jedisConnectionFactory) { - - var config = RedisVectorStoreConfig.builder() - .withIndexName(properties.getIndex()) - .withPrefix(properties.getPrefix()) - .build(); - - return new RedisVectorStore(config, embeddingModel, - new JedisPooled(jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()), - properties.isInitializeSchema()); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java deleted file mode 100644 index de80401ed1..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/vectorstore/RedisVectorStore.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright 2023 - 2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.ai.vectorstore; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ai.document.Document; -import org.springframework.ai.embedding.EmbeddingModel; -import org.springframework.ai.vectorstore.filter.FilterExpressionConverter; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.Pipeline; -import redis.clients.jedis.json.Path2; -import redis.clients.jedis.search.*; -import redis.clients.jedis.search.Schema.FieldType; -import redis.clients.jedis.search.schemafields.*; -import redis.clients.jedis.search.schemafields.VectorField.VectorAlgorithm; - -import java.text.MessageFormat; -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -/** - * The RedisVectorStore is for managing and querying vector data in a Redis database. It - * offers functionalities like adding, deleting, and performing similarity searches on - * documents. - * - * The store utilizes RedisJSON and RedisSearch to handle JSON documents and to index and - * search vector data. It supports various vector algorithms (e.g., FLAT, HSNW) for - * efficient similarity searches. Additionally, it allows for custom metadata fields in - * the documents to be stored alongside the vector and content data. - * - * This class requires a RedisVectorStoreConfig configuration object for initialization, - * which includes settings like Redis URI, index name, field names, and vector algorithms. - * It also requires an EmbeddingModel to convert documents into embeddings before storing - * them. - * - * @author Julien Ruaux - * @author Christian Tzolov - * @author Eddú Meléndez - * @see VectorStore - * @see RedisVectorStoreConfig - * @see EmbeddingModel - */ -public class RedisVectorStore implements VectorStore, InitializingBean { - - public enum Algorithm { - - FLAT, HSNW - - } - - public record MetadataField(String name, FieldType fieldType) { - - public static MetadataField text(String name) { - return new MetadataField(name, FieldType.TEXT); - } - - public static MetadataField numeric(String name) { - return new MetadataField(name, FieldType.NUMERIC); - } - - public static MetadataField tag(String name) { - return new MetadataField(name, FieldType.TAG); - } - - } - - /** - * Configuration for the Redis vector store. - */ - public static final class RedisVectorStoreConfig { - - private final String indexName; - - private final String prefix; - - private final String contentFieldName; - - private final String embeddingFieldName; - - private final Algorithm vectorAlgorithm; - - private final List metadataFields; - - private RedisVectorStoreConfig() { - this(builder()); - } - - private RedisVectorStoreConfig(Builder builder) { - this.indexName = builder.indexName; - this.prefix = builder.prefix; - this.contentFieldName = builder.contentFieldName; - this.embeddingFieldName = builder.embeddingFieldName; - this.vectorAlgorithm = builder.vectorAlgorithm; - this.metadataFields = builder.metadataFields; - } - - /** - * Start building a new configuration. - * @return The entry point for creating a new configuration. - */ - public static Builder builder() { - - return new Builder(); - } - - /** - * {@return the default config} - */ - public static RedisVectorStoreConfig defaultConfig() { - - return builder().build(); - } - - public static class Builder { - - private String indexName = DEFAULT_INDEX_NAME; - - private String prefix = DEFAULT_PREFIX; - - private String contentFieldName = DEFAULT_CONTENT_FIELD_NAME; - - private String embeddingFieldName = DEFAULT_EMBEDDING_FIELD_NAME; - - private Algorithm vectorAlgorithm = DEFAULT_VECTOR_ALGORITHM; - - private List metadataFields = new ArrayList<>(); - - private Builder() { - } - - /** - * Configures the Redis index name to use. - * @param name the index name to use - * @return this builder - */ - public Builder withIndexName(String name) { - this.indexName = name; - return this; - } - - /** - * Configures the Redis key prefix to use (default: "embedding:"). - * @param prefix the prefix to use - * @return this builder - */ - public Builder withPrefix(String prefix) { - this.prefix = prefix; - return this; - } - - /** - * Configures the Redis content field name to use. - * @param name the content field name to use - * @return this builder - */ - public Builder withContentFieldName(String name) { - this.contentFieldName = name; - return this; - } - - /** - * Configures the Redis embedding field name to use. - * @param name the embedding field name to use - * @return this builder - */ - public Builder withEmbeddingFieldName(String name) { - this.embeddingFieldName = name; - return this; - } - - /** - * Configures the Redis vector algorithmto use. - * @param algorithm the vector algorithm to use - * @return this builder - */ - public Builder withVectorAlgorithm(Algorithm algorithm) { - this.vectorAlgorithm = algorithm; - return this; - } - - public Builder withMetadataFields(MetadataField... fields) { - return withMetadataFields(Arrays.asList(fields)); - } - - public Builder withMetadataFields(List fields) { - this.metadataFields = fields; - return this; - } - - /** - * {@return the immutable configuration} - */ - public RedisVectorStoreConfig build() { - - return new RedisVectorStoreConfig(this); - } - - } - - } - - private final boolean initializeSchema; - - public static final String DEFAULT_INDEX_NAME = "spring-ai-index"; - - public static final String DEFAULT_CONTENT_FIELD_NAME = "content"; - - public static final String DEFAULT_EMBEDDING_FIELD_NAME = "embedding"; - - public static final String DEFAULT_PREFIX = "embedding:"; - - public static final Algorithm DEFAULT_VECTOR_ALGORITHM = Algorithm.HSNW; - - private static final String QUERY_FORMAT = "%s=>[KNN %s @%s $%s AS %s]"; - - private static final Path2 JSON_SET_PATH = Path2.of("$"); - - private static final String JSON_PATH_PREFIX = "$."; - - private static final Logger logger = LoggerFactory.getLogger(RedisVectorStore.class); - - private static final Predicate RESPONSE_OK = Predicate.isEqual("OK"); - - private static final Predicate RESPONSE_DEL_OK = Predicate.isEqual(1l); - - private static final String VECTOR_TYPE_FLOAT32 = "FLOAT32"; - - private static final String EMBEDDING_PARAM_NAME = "BLOB"; - - public static final String DISTANCE_FIELD_NAME = "vector_score"; - - private static final String DEFAULT_DISTANCE_METRIC = "COSINE"; - - private final JedisPooled jedis; - - private final EmbeddingModel embeddingModel; - - private final RedisVectorStoreConfig config; - - private FilterExpressionConverter filterExpressionConverter; - - public RedisVectorStore(RedisVectorStoreConfig config, EmbeddingModel embeddingModel, JedisPooled jedis, - boolean initializeSchema) { - - Assert.notNull(config, "Config must not be null"); - Assert.notNull(embeddingModel, "Embedding model must not be null"); - this.initializeSchema = initializeSchema; - - this.jedis = jedis; - this.embeddingModel = embeddingModel; - this.config = config; - this.filterExpressionConverter = new RedisFilterExpressionConverter(this.config.metadataFields); - } - - public JedisPooled getJedis() { - return this.jedis; - } - - @Override - public void add(List documents) { - try (Pipeline pipeline = this.jedis.pipelined()) { - for (Document document : documents) { - var embedding = this.embeddingModel.embed(document); - document.setEmbedding(embedding); - - var fields = new HashMap(); - fields.put(this.config.embeddingFieldName, embedding); - fields.put(this.config.contentFieldName, document.getContent()); - fields.putAll(document.getMetadata()); - pipeline.jsonSetWithEscape(key(document.getId()), JSON_SET_PATH, fields); - } - List responses = pipeline.syncAndReturnAll(); - Optional errResponse = responses.stream().filter(Predicate.not(RESPONSE_OK)).findAny(); - if (errResponse.isPresent()) { - String message = MessageFormat.format("Could not add document: {0}", errResponse.get()); - if (logger.isErrorEnabled()) { - logger.error(message); - } - throw new RuntimeException(message); - } - } - } - - private String key(String id) { - return this.config.prefix + id; - } - - @Override - public Optional delete(List idList) { - try (Pipeline pipeline = this.jedis.pipelined()) { - for (String id : idList) { - pipeline.jsonDel(key(id)); - } - List responses = pipeline.syncAndReturnAll(); - Optional errResponse = responses.stream().filter(Predicate.not(RESPONSE_DEL_OK)).findAny(); - if (errResponse.isPresent()) { - if (logger.isErrorEnabled()) { - logger.error("Could not delete document: {}", errResponse.get()); - } - return Optional.of(false); - } - return Optional.of(true); - } - } - - @Override - public List similaritySearch(SearchRequest request) { - - Assert.isTrue(request.getTopK() > 0, "The number of documents to returned must be greater than zero"); - Assert.isTrue(request.getSimilarityThreshold() >= 0 && request.getSimilarityThreshold() <= 1, - "The similarity score is bounded between 0 and 1; least to most similar respectively."); - - String filter = nativeExpressionFilter(request); - - String queryString = String.format(QUERY_FORMAT, filter, request.getTopK(), this.config.embeddingFieldName, - EMBEDDING_PARAM_NAME, DISTANCE_FIELD_NAME); - - List returnFields = new ArrayList<>(); - this.config.metadataFields.stream().map(MetadataField::name).forEach(returnFields::add); - returnFields.add(this.config.embeddingFieldName); - returnFields.add(this.config.contentFieldName); - returnFields.add(DISTANCE_FIELD_NAME); - var embedding = toFloatArray(this.embeddingModel.embed(request.getQuery())); - Query query = new Query(queryString).addParam(EMBEDDING_PARAM_NAME, RediSearchUtil.toByteArray(embedding)) - .returnFields(returnFields.toArray(new String[0])) - .setSortBy(DISTANCE_FIELD_NAME, true) - .dialect(2); - - SearchResult result = this.jedis.ftSearch(this.config.indexName, query); - return result.getDocuments() - .stream() - .filter(d -> similarityScore(d) >= request.getSimilarityThreshold()) - .map(this::toDocument) - .toList(); - } - - private Document toDocument(redis.clients.jedis.search.Document doc) { - var id = doc.getId().substring(this.config.prefix.length()); - var content = doc.hasProperty(this.config.contentFieldName) ? doc.getString(this.config.contentFieldName) - : null; - Map metadata = this.config.metadataFields.stream() - .map(MetadataField::name) - .filter(doc::hasProperty) - .collect(Collectors.toMap(Function.identity(), doc::getString)); - metadata.put(DISTANCE_FIELD_NAME, 1 - similarityScore(doc)); - return new Document(id, content, metadata); - } - - private float similarityScore(redis.clients.jedis.search.Document doc) { - return (2 - Float.parseFloat(doc.getString(DISTANCE_FIELD_NAME))) / 2; - } - - private String nativeExpressionFilter(SearchRequest request) { - if (request.getFilterExpression() == null) { - return "*"; - } - return "(" + this.filterExpressionConverter.convertExpression(request.getFilterExpression()) + ")"; - } - - @Override - public void afterPropertiesSet() { - - if (!this.initializeSchema) { - return; - } - - // If index already exists don't do anything - if (this.jedis.ftList().contains(this.config.indexName)) { - return; - } - - String response = this.jedis.ftCreate(this.config.indexName, - FTCreateParams.createParams().on(IndexDataType.JSON).addPrefix(this.config.prefix), schemaFields()); - if (!RESPONSE_OK.test(response)) { - String message = MessageFormat.format("Could not create index: {0}", response); - throw new RuntimeException(message); - } - } - - private Iterable schemaFields() { - Map vectorAttrs = new HashMap<>(); - vectorAttrs.put("DIM", this.embeddingModel.dimensions()); - vectorAttrs.put("DISTANCE_METRIC", DEFAULT_DISTANCE_METRIC); - vectorAttrs.put("TYPE", VECTOR_TYPE_FLOAT32); - List fields = new ArrayList<>(); - fields.add(TextField.of(jsonPath(this.config.contentFieldName)).as(this.config.contentFieldName).weight(1.0)); - fields.add(VectorField.builder() - .fieldName(jsonPath(this.config.embeddingFieldName)) - .algorithm(vectorAlgorithm()) - .attributes(vectorAttrs) - .as(this.config.embeddingFieldName) - .build()); - - if (!CollectionUtils.isEmpty(this.config.metadataFields)) { - for (MetadataField field : this.config.metadataFields) { - fields.add(schemaField(field)); - } - } - return fields; - } - - private SchemaField schemaField(MetadataField field) { - String fieldName = jsonPath(field.name); - switch (field.fieldType) { - case NUMERIC: - return NumericField.of(fieldName).as(field.name); - case TAG: - return TagField.of(fieldName).as(field.name); - case TEXT: - return TextField.of(fieldName).as(field.name); - default: - throw new IllegalArgumentException( - MessageFormat.format("Field {0} has unsupported type {1}", field.name, field.fieldType)); - } - } - - private VectorAlgorithm vectorAlgorithm() { - if (config.vectorAlgorithm == Algorithm.HSNW) { - return VectorAlgorithm.HNSW; - } - return VectorAlgorithm.FLAT; - } - - private String jsonPath(String field) { - return JSON_PATH_PREFIX + field; - } - - private static float[] toFloatArray(List embeddingDouble) { - float[] embeddingFloat = new float[embeddingDouble.size()]; - int i = 0; - for (Double d : embeddingDouble) { - embeddingFloat[i++] = d.floatValue(); - } - return embeddingFloat; - } - -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/AzureOpenAIChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/AzureOpenAIChatModelTests.java index c859587799..7713ec4688 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/AzureOpenAIChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/AzureOpenAIChatModelTests.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.framework.ai.chat; -import com.azure.ai.openai.OpenAIClient; import com.azure.ai.openai.OpenAIClientBuilder; import com.azure.core.credential.AzureKeyCredential; import com.azure.core.util.ClientOptions; @@ -27,13 +26,13 @@ import static org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatP */ public class AzureOpenAIChatModelTests { - private final OpenAIClient openAiApi = (new OpenAIClientBuilder()) + // TODO @芋艿:晚点在调整 + private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder() .endpoint("https://eastusprejade.openai.azure.com") .credential(new AzureKeyCredential("xxx")) - .clientOptions((new ClientOptions()).setApplicationId("spring-ai")) - .buildClient(); + .clientOptions((new ClientOptions()).setApplicationId("spring-ai")); private final AzureOpenAiChatModel chatModel = new AzureOpenAiChatModel(openAiApi, - AzureOpenAiChatOptions.builder().withDeploymentName(DEFAULT_DEPLOYMENT_NAME).build()); + AzureOpenAiChatOptions.builder().deploymentName(DEFAULT_DEPLOYMENT_NAME).build()); @Test @Disabled diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/LlamaChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/LlamaChatModelTests.java index c6b99f287b..a72b556fcd 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/LlamaChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/LlamaChatModelTests.java @@ -1,20 +1,6 @@ package cn.iocoder.yudao.framework.ai.chat; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.ai.chat.messages.Message; -import org.springframework.ai.chat.messages.SystemMessage; -import org.springframework.ai.chat.messages.UserMessage; -import org.springframework.ai.chat.model.ChatResponse; -import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.ollama.OllamaChatModel; -import org.springframework.ai.ollama.api.OllamaApi; -import org.springframework.ai.ollama.api.OllamaModel; -import org.springframework.ai.ollama.api.OllamaOptions; -import reactor.core.publisher.Flux; - -import java.util.ArrayList; -import java.util.List; /** * {@link OllamaChatModel} 集成测试 @@ -23,41 +9,41 @@ import java.util.List; */ public class LlamaChatModelTests { - private final OllamaApi ollamaApi = new OllamaApi( - "http://127.0.0.1:11434"); - private final OllamaChatModel chatModel = new OllamaChatModel(ollamaApi, - OllamaOptions.create().withModel(OllamaModel.LLAMA3.getModelName())); - - @Test - @Disabled - public void testCall() { - // 准备参数 - List messages = new ArrayList<>(); - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); - messages.add(new UserMessage("1 + 1 = ?")); - - // 调用 - ChatResponse response = chatModel.call(new Prompt(messages)); - // 打印结果 - System.out.println(response); - System.out.println(response.getResult().getOutput()); - } - - @Test - @Disabled - public void testStream() { - // 准备参数 - List messages = new ArrayList<>(); - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); - messages.add(new UserMessage("1 + 1 = ?")); - - // 调用 - Flux flux = chatModel.stream(new Prompt(messages)); - // 打印结果 - flux.doOnNext(response -> { -// System.out.println(response); - System.out.println(response.getResult().getOutput()); - }).then().block(); - } +// private final OllamaApi ollamaApi = new OllamaApi( +// "http://127.0.0.1:11434"); +// private final OllamaChatModel chatModel = new OllamaChatModel(ollamaApi, +// OllamaOptions.create().withModel(OllamaModel.LLAMA3.getModelName())); +// +// @Test +// @Disabled +// public void testCall() { +// // 准备参数 +// List messages = new ArrayList<>(); +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); +// messages.add(new UserMessage("1 + 1 = ?")); +// +// // 调用 +// ChatResponse response = chatModel.call(new Prompt(messages)); +// // 打印结果 +// System.out.println(response); +// System.out.println(response.getResult().getOutput()); +// } +// +// @Test +// @Disabled +// public void testStream() { +// // 准备参数 +// List messages = new ArrayList<>(); +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); +// messages.add(new UserMessage("1 + 1 = ?")); +// +// // 调用 +// Flux flux = chatModel.stream(new Prompt(messages)); +// // 打印结果 +// flux.doOnNext(response -> { +//// System.out.println(response); +// System.out.println(response.getResult().getOutput()); +// }).then().block(); +// } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/OpenAIChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/OpenAIChatModelTests.java index 6768325462..b6653b1b15 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/OpenAIChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/OpenAIChatModelTests.java @@ -26,7 +26,7 @@ public class OpenAIChatModelTests { "https://api.holdai.top", "sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf"); private final OpenAiChatModel chatModel = new OpenAiChatModel(openAiApi, - OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_O).build()); + OpenAiChatOptions.builder().model(OpenAiApi.ChatModel.GPT_4_O).build()); @Test @Disabled diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/YiYanChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/YiYanChatModelTests.java index d10a04677f..e271decbc5 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/YiYanChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/YiYanChatModelTests.java @@ -25,7 +25,7 @@ public class YiYanChatModelTests { "qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e"); private final QianFanChatModel chatModel = new QianFanChatModel(qianFanApi, - QianFanChatOptions.builder().withModel(QianFanApi.ChatModel.ERNIE_Tiny_8K.getValue()).build() + QianFanChatOptions.builder().model(QianFanApi.ChatModel.ERNIE_Tiny_8K.getValue()).build() ); @Test diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/ZhiPuAiChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/ZhiPuAiChatModelTests.java index 20b73bd8e7..101be34b7b 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/ZhiPuAiChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/ZhiPuAiChatModelTests.java @@ -24,7 +24,7 @@ public class ZhiPuAiChatModelTests { private final ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi("32f84543e54eee31f8d56b2bd6020573.3vh9idLJZ2ZhxDEs"); private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(zhiPuAiApi, - ZhiPuAiChatOptions.builder().withModel(ZhiPuAiApi.ChatModel.GLM_4.getModelName()).build()); + ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.ChatModel.GLM_4.getName()).build()); @Test @Disabled diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/QianFanImageTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/QianFanImageTests.java index 22bf6614eb..4c0a9eb36d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/QianFanImageTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/QianFanImageTests.java @@ -25,9 +25,9 @@ public class QianFanImageTests { // 准备参数 // 只支持 1024x1024、768x768、768x1024、1024x768、576x1024、1024x576 QianFanImageOptions imageOptions = QianFanImageOptions.builder() - .withModel(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue()) - .withWidth(1024).withHeight(1024) - .withN(1) + .model(QianFanImageApi.ImageModel.Stable_Diffusion_XL.getValue()) + .width(1024).height(1024) + .N(1) .build(); ImagePrompt prompt = new ImagePrompt("good", imageOptions); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java index 41d7859c4d..cc62f02fb6 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java @@ -1,34 +1,30 @@ package cn.iocoder.yudao.framework.ai.image; -import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel; +import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; +import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; -import com.alibaba.dashscope.utils.Constants; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.image.ImageOptions; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; -import org.springframework.ai.openai.OpenAiImageOptions; /** - * {@link com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel} 集成测试类 + * {@link DashScopeImageModel} 集成测试类 * * @author fansili */ public class TongYiImagesModelTest { - private final ImageSynthesis imageApi = new ImageSynthesis(); - private final TongYiImagesModel imageModel = new TongYiImagesModel(imageApi); - - static { - Constants.apiKey = "sk-Zsd81gZYg7"; - } + private final DashScopeImageModel imageModel = new DashScopeImageModel( + new DashScopeImageApi("sk-7d903764249848cfa912733146da12d1")); @Test @Disabled public void imageCallTest() { // 准备参数 - ImageOptions options = OpenAiImageOptions.builder() + ImageOptions options = DashScopeImageOptions.builder() .withModel(ImageSynthesis.Models.WANX_V1) .withHeight(256).withWidth(256) .build(); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/ZhiPuAiImageModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/ZhiPuAiImageModelTests.java index f9338995f3..f4802d4d21 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/ZhiPuAiImageModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/ZhiPuAiImageModelTests.java @@ -22,7 +22,7 @@ public class ZhiPuAiImageModelTests { public void testCall() { // 准备参数 ZhiPuAiImageOptions imageOptions = ZhiPuAiImageOptions.builder() - .withModel(ZhiPuAiImageApi.ImageModel.CogView_3.getValue()) + .model(ZhiPuAiImageApi.ImageModel.CogView_3.getValue()) .build(); ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions); From 6d059eae61f1ff74ea43dd45aabd5a55de01a7f0 Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Sat, 22 Feb 2025 16:58:47 +0800 Subject: [PATCH 191/309] =?UTF-8?q?[fix]=EF=BC=9Astatistics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/IotStatisticsController.java | 55 +++++++++++++ .../statistics/vo/IotStatisticsRespVO.java | 78 +++++++++++++++++++ .../iot/dal/mysql/device/IotDeviceMapper.java | 23 ++++++ .../product/IotProductCategoryMapper.java | 13 ++++ .../dal/mysql/product/IotProductMapper.java | 26 +++++++ .../mysql/thingmodel/IotThingModelMapper.java | 12 +++ .../iot/service/device/IotDeviceService.java | 26 ++++++- .../service/device/IotDeviceServiceImpl.java | 15 ++++ .../product/IotProductCategoryService.java | 17 ++++ .../IotProductCategoryServiceImpl.java | 59 +++++++++++++- .../service/product/IotProductService.java | 17 ++++ .../product/IotProductServiceImpl.java | 11 +++ .../thingmodel/IotThingModelService.java | 9 +++ .../thingmodel/IotThingModelServiceImpl.java | 6 ++ .../src/main/resources/application-local.yaml | 6 +- 15 files changed, 368 insertions(+), 5 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java new file mode 100644 index 0000000000..4262f31cca --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.iot.controller.admin.statistics; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; +import cn.iocoder.yudao.module.iot.service.thingmodel.IotThingModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; +import java.util.List; + + +@Tag(name = "管理后台 - IoT 数据统计") +@RestController +@RequestMapping("/iot/statistics") +@Validated +public class IotStatisticsController { + + @Resource + private IotDeviceService iotDeviceService; + + @Resource + private IotProductCategoryService iotProductCategoryService; + + @Resource + private IotProductService iotProductService; + + + @GetMapping("/count") + @Operation(summary = "获取IOT首页的数据统计", description = "主要用于IOT首页的数据统计") + public CommonResult getSimpleProductList(){ + IotStatisticsRespVO iotStatisticsRespVO = new IotStatisticsRespVO(); + // 获取总数 + iotStatisticsRespVO.setCategoryTotal(iotProductCategoryService.getProductCategoryCount(null)); + iotStatisticsRespVO.setProductTotal(iotProductService.getProductCount(null)); + iotStatisticsRespVO.setDeviceTotal(iotDeviceService.getDeviceCount(null)); + + // 获取今日新增数量 + LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); + iotStatisticsRespVO.setCategoryTodayTotal(iotProductCategoryService.getProductCategoryCount(todayStart)); + iotStatisticsRespVO.setProductTodayTotal(iotProductService.getProductCount(todayStart)); + iotStatisticsRespVO.setDeviceTodayTotal(iotDeviceService.getDeviceCount(todayStart)); + + return CommonResult.success(iotStatisticsRespVO); + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java new file mode 100644 index 0000000000..a72c0536e5 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - Iot统计 Response VO") +@Data +public class IotStatisticsRespVO { + + @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private long categoryTotal; + + @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private long productTotal; + + @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private long deviceTotal; + + @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private long reportTotal; + + @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private long categoryTodayTotal; + + @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + private long productTodayTotal; + + @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private long deviceTodayTotal; + + @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") + private long reportTodayTotal; + + @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") + private long onlineTotal; + + @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") + private long offlineTotal; + + @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + private long neverOnlineTotal; + + @Schema(description = "上报数据数量统计") + private List reportDataStats; + + @Schema(description = "上行数据数量统计") + private List deviceUpMessageStats; + + @Schema(description = "下行数据数量统计") + private List deviceDownMessageStats; + + @Schema(description = "按品类统计的设备数量") + private List deviceStatsOfCategory; + + @Schema(description = "时间数据") + @Data + public static class TimeData { + + @Schema(description = "时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1646092800000") + private long time; + + @Schema(description = "数据值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + private Object data; + } + + @Schema(description = "数据项") + @Data + public static class DataItem { + + @Schema(description = "数据项名", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能家居") + private String name; + + @Schema(description = "数据项值", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") + private Object value; + } +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index a3ae4e3807..e57eb8f7c0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; +import java.time.LocalDateTime; import java.util.List; /** @@ -66,4 +67,26 @@ public interface IotDeviceMapper extends BaseMapperX { .orderByDesc(IotDeviceDO::getId)); } + /** + * 统计设备数量 + * + * @param createTime 创建时间,如果为空,则统计所有设备数量 + * @return 设备数量 + */ + default Long selectCountByCreateTime(LocalDateTime createTime) { + return selectCount(new LambdaQueryWrapperX() + .geIfPresent(IotDeviceDO::getCreateTime, createTime)); + } + + /** + * 统计指定状态的设备数量 + * + * @param state 状态 + * @return 设备数量 + */ + default Long selectCountByState(Integer state) { + return selectCount(new LambdaQueryWrapperX() + .eqIfPresent(IotDeviceDO::getState, state)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java index 70ad56da80..6e9deb4eba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java @@ -6,7 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import java.time.LocalDateTime; import java.util.List; /** @@ -28,4 +30,15 @@ public interface IotProductCategoryMapper extends BaseMapperX() + .geIfPresent(IotProductCategoryDO::getCreateTime, createTime)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index 0f56251003..11a2551610 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -8,6 +8,9 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; +import java.time.LocalDateTime; +import java.util.List; + /** * IoT 产品 Mapper * @@ -28,4 +31,27 @@ public interface IotProductMapper extends BaseMapperX { .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); } + /** + * 统计产品数量 + * + * @param createTime 创建时间,如果为空,则统计所有产品数量 + * @return 产品数量 + */ + default Long selectCountByCreateTime(LocalDateTime createTime) { + return selectCount(new LambdaQueryWrapperX() + .geIfPresent(IotProductDO::getCreateTime, createTime)); + } + + /** + * 获得产品列表,基于分类编号 + * + * @param categoryId 分类编号 + * @return 产品列表 + */ + default List selectListByCategoryId(Long categoryId) { + return selectList(new LambdaQueryWrapperX() + .eq(IotProductDO::getCategoryId, categoryId) + .orderByDesc(IotProductDO::getId)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java index 4c563c65eb..21972343fc 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelP import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import org.apache.ibatis.annotations.Mapper; +import java.time.LocalDateTime; import java.util.List; /** @@ -72,4 +73,15 @@ public interface IotThingModelMapper extends BaseMapperX { IotThingModelDO::getName, name); } + /** + * 统计物模型数量 + * + * @param createTime 创建时间,如果为空,则统计所有物模型数量 + * @return 物模型数量 + */ + default Long selectCountByCreateTime(LocalDateTime createTime) { + return selectCount(new LambdaQueryWrapperX() + .geIfPresent(IotThingModelDO::getCreateTime, createTime)); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 24532d2548..fff1368c62 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -7,6 +7,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import javax.annotation.Nullable; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; @@ -109,7 +110,7 @@ public interface IotDeviceService { IotDeviceDO getDeviceByDeviceKey(String deviceKey); /** - * ��得设备分页 + * 获得设备分页 * * @param pageReqVO 分页查询 * @return IoT 设备分页 @@ -168,4 +169,27 @@ public interface IotDeviceService { */ IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport); + /** + * 获得设备数量 + * + * @param createTime 创建时间,如果为空,则统计所有设备数量 + * @return 设备数量 + */ + Long getDeviceCount(LocalDateTime createTime); + + /** + * 获得设备数量,基于状态 + * + * @param state 状态 + * @return 设备数量 + */ + Long getDeviceCountByState(Integer state); + + /** + * 获得所有设备列表 + * + * @return 设备列表 + */ + List getDeviceList(); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 66854e84b8..52766d37ab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -434,4 +434,19 @@ public class IotDeviceServiceImpl implements IotDeviceService { return SpringUtil.getBean(getClass()); } + @Override + public Long getDeviceCount(LocalDateTime createTime) { + return deviceMapper.selectCountByCreateTime(createTime); + } + + @Override + public Long getDeviceCountByState(Integer state) { + return deviceMapper.selectCountByState(state); + } + + @Override + public List getDeviceList() { + return deviceMapper.selectList(); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java index e44b6d4878..546c0231a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProdu import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import jakarta.validation.Valid; +import java.time.LocalDateTime; import java.util.Collection; import java.util.List; import java.util.Map; @@ -83,4 +84,20 @@ public interface IotProductCategoryService { */ List getProductCategoryListByStatus(Integer status); + /** + * 获得产品分类数量 + * + * @param createTime 创建时间,如果为空,则统计所有分类数量 + * @return 产品分类数量 + */ + Long getProductCategoryCount(LocalDateTime createTime); + + + /** + * 获得各产品分类下属的设备总数 + * + * @return 产品分类名称和各产品分类下属的设备总数 + */ + Map getDeviceCountsOfProductCategoryMap(); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index c531b14504..683dad1b7b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -5,14 +5,20 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.time.LocalDateTime; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS; @@ -29,7 +35,12 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService @Resource private IotProductCategoryMapper productCategoryMapper; - @Override + @Resource + private IotProductService productService; + + @Resource + private IotDeviceService deviceService; + public Long createProductCategory(IotProductCategorySaveReqVO createReqVO) { // 插入 IotProductCategoryDO productCategory = BeanUtils.toBean(createReqVO, IotProductCategoryDO.class); @@ -84,4 +95,50 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService return productCategoryMapper.selectListByStatus(status); } + @Override + public Long getProductCategoryCount(LocalDateTime createTime) { + return productCategoryMapper.selectCountByCreateTime(createTime); + } + + @Override + public Map getDeviceCountsOfProductCategoryMap() { + // 1. 获取所有数据 + List categoryList = productCategoryMapper.selectList(); + List productList = productService.getProductList(); + List deviceList = deviceService.getDeviceList(); + + // 2. 统计每个分类下的设备数量 + Map categoryDeviceCountMap = new HashMap<>(); + + // 2.1 初始化所有分类的计数为0 + for (IotProductCategoryDO category : categoryList) { + categoryDeviceCountMap.put(category.getName(), 0); + } + + // 2.2 构建产品ID到分类的映射 + Map productCategoryMap = new HashMap<>(); + for (IotProductDO product : productList) { + Long categoryId = product.getCategoryId(); + IotProductCategoryDO category = categoryList.stream() + .filter(c -> c.getId().equals(categoryId)) + .findFirst() + .orElse(null); + if (category != null) { + productCategoryMap.put(product.getId(), category); + } + } + + // 2.3 统计每个分类下的设备数量 + for (IotDeviceDO device : deviceList) { + Long productId = device.getProductId(); + IotProductCategoryDO category = productCategoryMap.get(productId); + if (category != null) { + String categoryName = category.getName(); + categoryDeviceCountMap.merge(categoryName, 1, Integer::sum); + } + } + + return categoryDeviceCountMap; + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index a317cacae9..590db15a8d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import jakarta.validation.Valid; +import java.time.LocalDateTime; import java.util.List; /** @@ -92,4 +93,20 @@ public interface IotProductService { */ List getProductList(); + /** + * 获得产品数量 + * + * @param createTime 创建时间,如果为空,则统计所有产品数量 + * @return 产品数量 + */ + Long getProductCount(LocalDateTime createTime); + + /** + * 获得产品列表,基于分类编号 + * + * @param categoryId 分类编号 + * @return 产品列表 + */ + List getProductListByCategoryId(Long categoryId); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index b77a7e0e1b..848da74d65 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -15,6 +15,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.time.LocalDateTime; import java.util.List; import java.util.Objects; @@ -137,4 +138,14 @@ public class IotProductServiceImpl implements IotProductService { return productMapper.selectList(); } + @Override + public Long getProductCount(LocalDateTime createTime) { + return productMapper.selectCountByCreateTime(createTime); + } + + @Override + public List getProductListByCategoryId(Long categoryId) { + return productMapper.selectListByCategoryId(categoryId); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index 92aa5978cf..6eb8bb346d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.thingmodel.vo.IotThingModelS import cn.iocoder.yudao.module.iot.dal.dataobject.thingmodel.IotThingModelDO; import jakarta.validation.Valid; +import java.time.LocalDateTime; import java.util.List; /** @@ -80,4 +81,12 @@ public interface IotThingModelService { */ List getThingModelList(IotThingModelListReqVO reqVO); + /** + * 获得物模型数量 + * + * @param createTime 创建时间,如果为空,则统计所有物模型数量 + * @return 物模型数量 + */ + Long getThingModelCount(LocalDateTime createTime); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index b9167e4ef3..813b5ea585 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -29,6 +29,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -363,4 +364,9 @@ public class IotThingModelServiceImpl implements IotThingModelService { return SpringUtil.getBean(getClass()); } + @Override + public Long getThingModelCount(LocalDateTime createTime) { + return thingModelMapper.selectCountByCreateTime(createTime); + } + } diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index bbb94772c7..047238dd20 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -78,10 +78,10 @@ spring: # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: redis: - host: 127.0.0.1 # 地址 + host: chaojiniu.top # 地址 port: 6379 # 端口 - database: 1 # 数据库索引 - # password: 123456 # 密码,建议生产环境开启 + database: 15 # 数据库索引 + password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### From fa40ae1dbd368628a9e8a0ef0b6e0636be61ee45 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 18:21:18 +0800 Subject: [PATCH 192/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AMQTT=20=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/api/device/IotDeviceUpstreamApi.java | 1 + .../control/upstream/IotDeviceEmqxAuthReqDTO.java | 1 + .../controller/admin/device/IotDeviceController.java | 1 + .../iot/service/device/IotDeviceServiceImpl.java | 4 +--- .../device/control/IotDeviceUpstreamServiceImpl.java | 2 ++ .../iocoder/yudao/module/iot/util/MqttSignUtils.java | 12 +++++++++--- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index c43a0f2b23..e88706ac59 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -71,6 +71,7 @@ public interface IotDeviceUpstreamApi { @PostMapping(PREFIX + "/add-topology") CommonResult addDeviceTopology(@Valid @RequestBody IotDeviceTopologyAddReqDTO addReqDTO); + // TODO @芋艿:考虑 http 认证 /** * 认证 Emqx 连接 * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java index 365552db0f..2fb10a0765 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; import jakarta.validation.constraints.NotEmpty; import lombok.Data; +// TODO @芋艿:要不要继承 IotDeviceUpstreamAbstractReqDTO /** * IoT 认证 Emqx 连接 Request DTO * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 18aa5a34fa..2add4ee133 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -177,6 +177,7 @@ public class IotDeviceController { return success(true); } + // TODO @haohao:是不是默认详情接口,不返回 secret,然后这个接口,用于统一返回。然后接口名可以更通用一点。 @GetMapping("/mqtt-connection-params") @Operation(summary = "获取 MQTT 连接参数") @PreAuthorize("@ss.hasPermission('iot:device:mqtt-connection-params')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 847581512b..66d3cf861d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -123,9 +123,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { private void initDevice(IotDeviceDO device, IotProductDO product) { device.setProductId(product.getId()).setProductKey(product.getProductKey()) .setDeviceType(product.getDeviceType()); - // 生成并设置必要的字段 - // TODO @芋艿:各种 mqtt 是不是可以简化! - // clientId、username、password 根据规则实时生成 + // 生成密钥 device.setDeviceSecret(generateDeviceSecret()); // 设置设备状态为未激活 device.setState(IotDeviceStateEnum.INACTIVE.getState()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index 93b4ea3888..9ce0efbb1e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -280,6 +280,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { sendDeviceMessage(message, device); } + // TODO @haohao:建议返回 boolean; @Override public Boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { log.info("[authenticateEmqxConnection][认证 Emqx 连接: {}]", authReqDTO); @@ -303,6 +304,7 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { String deviceSecret = device.getDeviceSecret(); String clientId = authReqDTO.getClientId(); MqttSignResult sign = MqttSignUtils.calculate(productKey, deviceName, deviceSecret, clientId); + // TODO @haohao:notEquals,尽量不走取反逻辑哈 if (!StrUtil.equals(sign.getPassword(), authReqDTO.getPassword())) { log.error("[authenticateEmqxConnection][认证失败,密码不正确]"); return Boolean.FALSE; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java index 40213e3aee..bf364c53ea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java @@ -8,12 +8,15 @@ import java.nio.charset.StandardCharsets; /** * MQTT 签名工具类 - * 提供静态方法来计算 MQTT 连接参数。 + * + * 提供静态方法来计算 MQTT 连接参数 */ public class MqttSignUtils { private static final String SIGN_METHOD = "hmacsha256"; + // TODO @haohao:calculate 方法,可以融合么? + /** * 计算 MQTT 连接参数 * @@ -25,11 +28,11 @@ public class MqttSignUtils { public static MqttSignResult calculate(String productKey, String deviceName, String deviceSecret) { String clientId = productKey + "." + deviceName; String username = deviceName + "&" + productKey; + // 生成 password + // TODO @haohao:signContent 和 signContentBuilder 风格保持统一的实现哈 String signContent = String.format("clientId%sdeviceName%sdeviceSecret%sproductKey%s", clientId, deviceName, deviceSecret, productKey); - String password = sign(signContent, deviceSecret); - return new MqttSignResult(clientId, username, password); } @@ -54,6 +57,7 @@ public class MqttSignUtils { return new MqttSignResult(clientId, username, password); } + // TODO @haohao:hutool 貌似有工具类可以用哈。 private static String sign(String content, String key) { try { Mac mac = Mac.getInstance(SIGN_METHOD); @@ -81,7 +85,9 @@ public class MqttSignUtils { * MQTT 签名结果类 */ @Getter + // TODO @haohao:可以用 lombok 哈 public static class MqttSignResult { + private final String clientId; private final String username; private final String password; From f76843573ee25546b829b5a1209bba9b2ffcb12f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 18:31:09 +0800 Subject: [PATCH 193/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B0=E6=8D=AE=E6=A1=A5?= =?UTF-8?q?=E6=A2=81=E7=9A=84=E6=89=A7=E8=A1=8C=E5=99=A8=E6=8A=BD=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/action/IotRuleSceneDataBridgeAction.java | 7 ++----- .../databridge}/IotDataBridgeExecute.java | 2 +- .../databridge}/IotHttpDataBridgeExecute.java | 2 +- .../databridge}/IotRocketMQDataBridgeExecute.java | 12 ++++++++---- 4 files changed, 12 insertions(+), 11 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/{execute => action/databridge}/IotDataBridgeExecute.java (92%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/{execute => action/databridge}/IotHttpDataBridgeExecute.java (98%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/{execute => action/databridge}/IotRocketMQDataBridgeExecute.java (92%) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index f87cd60011..6733331cb4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotRuleSceneDO; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneActionTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; -import cn.iocoder.yudao.module.iot.service.rule.execute.IotDataBridgeExecute; +import cn.iocoder.yudao.module.iot.service.rule.action.databridge.IotDataBridgeExecute; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -19,9 +19,6 @@ import java.util.List; * * @author 芋道源码 */ -// TODO @芋艿:【优化】因为 bridge 会比较多,所以可以考虑在 rule 下,新建一个 bridge 的 package,然后定义一个 bridgehandler,它有: -// 1. input 方法、output 方法 -// 2. build 方法,用于有状态的连接,例如说 mq、tcp、websocket @Component @Slf4j public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { @@ -49,7 +46,7 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { return; } - // 2.1 执行数据桥接操作 + // 2. 执行数据桥接操作 dataBridgeExecutes.forEach(execute -> execute.execute(message, dataBridge)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java similarity index 92% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index 8979d21150..cd00f4f3e7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service.rule.execute; +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java similarity index 98% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java index 861fadd6c0..76f1b793f6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotHttpDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service.rule.execute; +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java similarity index 92% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index 11ca2339cf..626208e187 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/execute/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service.rule.execute; +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; @@ -30,12 +30,13 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { /** * 针对 {@link IotDataBridgeDO.RocketMQConfig} 的 DefaultMQProducer 缓存 */ + // TODO @puhui999:因为 kafka 之类也存在这个情况,是不是得搞个抽象类。提供一个 initProducer,和 closeProducer 方法 private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() - // 只阻塞当前数据加载线程,其他线程返回旧值 - .refreshAfterWrite(Duration.ofMinutes(10)) + .refreshAfterWrite(Duration.ofMinutes(10)) // TODO puhui999:应该是 read 30 分钟哈 // 增加移除监听器,自动关闭 producer .removalListener(notification -> { DefaultMQProducer producer = (DefaultMQProducer) notification.getValue(); + // TODO puhui999:if return,更简短哈 if (producer != null) { try { producer.shutdown(); @@ -45,8 +46,10 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { } } }) + // TODO @puhui999:就同步哈,不用异步处理。 // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 .build(CacheLoader.asyncReloading(new CacheLoader() { + @Override public DefaultMQProducer load(IotDataBridgeDO.RocketMQConfig config) throws Exception { DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); @@ -55,6 +58,7 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); return producer; } + }, Executors.newCachedThreadPool())); @Override @@ -120,7 +124,7 @@ public class IotRocketMQDataBridgeExecute implements IotDataBridgeExecute { log.info("[main][第一次执行,应该会创建新的 producer]"); action.executeRocketMQ(message, config); - log.info("[main][第二次执行,应该会复用缓存的 producer]"); + log.info("[main][第二次执行,应该会复用缓存的 producer]"); action.executeRocketMQ(message, config); } From 8cf8af1f6d2dc490da99e386afc4775f3d36c36d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 19:51:13 +0800 Subject: [PATCH 194/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E5=9B=BA=E4=BB=B6=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 3 ++ .../admin/ota/IotOtaFirmwareController.java | 10 ++--- .../ota/IotOtaUpgradeRecordController.java | 19 ++++---- .../ota/IotOtaUpgradeTaskController.java | 1 + .../firmware/IotOtaFirmwareCommonReqVO.java | 1 + .../firmware/IotOtaFirmwareCreateReqVO.java | 8 +++- .../vo/firmware/IotOtaFirmwarePageReqVO.java | 2 +- .../ota/vo/firmware/IotOtaFirmwareRespVO.java | 2 +- .../record/IotOtaUpgradeRecordPageReqVO.java | 1 + .../task/IotOtaUpgradeTaskSaveReqVO.java | 1 + .../ota/IotOtaUpgradeRecordConvert.java | 3 +- .../dataobject/ota/IotOtaUpgradeTaskDO.java | 2 + .../iot/dal/mysql/device/IotDeviceMapper.java | 3 +- .../dal/mysql/ota/IotOtaFirmwareMapper.java | 15 +++---- .../mysql/ota/IotOtaUpgradeRecordMapper.java | 4 +- .../mysql/ota/IotOtaUpgradeTaskMapper.java | 9 ++-- .../iot/job/ota/IotOtaUpgradeRecordJob.java | 5 ++- .../iot/job/ota/IotOtaUpgradeTaskJob.java | 1 + .../service/ota/IotOtaFirmwareService.java | 1 + .../ota/IotOtaFirmwareServiceImpl.java | 19 +++++--- .../ota/IotOtaUpgradeRecordService.java | 22 ++++----- .../ota/IotOtaUpgradeRecordServiceImpl.java | 23 ++++++---- .../service/ota/IotOtaUpgradeTaskService.java | 1 + .../ota/IotOtaUpgradeTaskServiceImpl.java | 45 ++++++++++++------- .../IotOtaUpgradeRecordCreateReqBO.java | 2 +- .../IotOtaUpgradeRecordUpdateReqBO.java | 3 +- .../iot/service/ota/bo/package-info.java | 1 - .../service/ota/bo/upgrade/package-info.java | 1 - .../mapper/ota/IotOtaUpgradeRecordMapper.xml | 2 + 29 files changed, 128 insertions(+), 82 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/{upgrade/record => }/IotOtaUpgradeRecordCreateReqBO.java (96%) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/{upgrade/record => }/IotOtaUpgradeRecordUpdateReqBO.java (93%) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 96e01394ab..74fbb8b2e5 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -52,14 +52,17 @@ public interface ErrorCodeConstants { // ========== 插件实例 1-050-007-000 ========== // ========== 固件相关 1-050-008-000 ========== + ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在"); ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复"); + // TODO @li:1_050_008_100,这样有点间隔? ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_002, "升级任务不存在"); ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_003, "升级任务名称重复"); ErrorCode OTA_UPGRADE_TASK_PARAMS_INVALID = new ErrorCode(1_050_008_004, "升级任务参数无效"); ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_005, "升级任务不能取消"); + // TODO @li:1_050_008_200 ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_006, "升级记录不存在"); ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_007, "升级记录重复"); ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_008, "升级记录不能重试"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java index 2771e35b58..344a1e3fc9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java @@ -21,7 +21,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Validated @RestController -@Tag(name = "管理后台 - IoT OTA固件") +@Tag(name = "管理后台 - IoT OTA 固件") @RequestMapping("/iot/ota-firmware") public class IotOtaFirmwareController { @@ -29,14 +29,14 @@ public class IotOtaFirmwareController { private IotOtaFirmwareService otaFirmwareService; @PostMapping("/create") - @Operation(summary = "创建OTA固件") + @Operation(summary = "创建 OTA 固件") @PreAuthorize("@ss.hasPermission('iot:ota-firmware:create')") public CommonResult createOtaFirmware(@Valid @RequestBody IotOtaFirmwareCreateReqVO createReqVO) { return success(otaFirmwareService.createOtaFirmware(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新OTA固件") + @Operation(summary = "更新 OTA 固件") @PreAuthorize("@ss.hasPermission('iot:ota-firmware:update')") public CommonResult updateOtaFirmware(@Valid @RequestBody IotOtaFirmwareUpdateReqVO updateReqVO) { otaFirmwareService.updateOtaFirmware(updateReqVO); @@ -44,7 +44,7 @@ public class IotOtaFirmwareController { } @GetMapping("/get") - @Operation(summary = "获得OTA固件") + @Operation(summary = "获得 OTA 固件") @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") public CommonResult getOtaFirmware(@RequestParam("id") Long id) { IotOtaFirmwareDO otaFirmware = otaFirmwareService.getOtaFirmware(id); @@ -52,7 +52,7 @@ public class IotOtaFirmwareController { } @GetMapping("/page") - @Operation(summary = "获得OTA固件分页") + @Operation(summary = "获得 OTA 固件分页") @PreAuthorize("@ss.hasPermission('iot:ota-firmware:query')") public CommonResult> getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO) { PageResult pageResult = otaFirmwareService.getOtaFirmwarePage(pageReqVO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java index e28f721c81..dcff396480 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java @@ -22,21 +22,13 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Validated @RestController -@Tag(name = "管理后台 - OTA升级记录") +@Tag(name = "管理后台 - OTA 升级记录") @RequestMapping("/iot/ota-upgrade-record") public class IotOtaUpgradeRecordController { @Resource private IotOtaUpgradeRecordService upgradeRecordService; - @GetMapping("/get-count") - @Operation(summary = "获得升级记录 分页 tab count") - @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") - public CommonResult> getOtaUpgradeRecordCount( - @Valid IotOtaUpgradeRecordPageReqVO pageReqVO) { - return success(upgradeRecordService.getOtaUpgradeRecordCount(pageReqVO)); - } - @GetMapping("/get-statistics") @Operation(summary = "固件升级设备统计") @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") @@ -46,6 +38,14 @@ public class IotOtaUpgradeRecordController { return success(upgradeRecordService.getOtaUpgradeRecordStatistics(firmwareId)); } + @GetMapping("/get-count") + @Operation(summary = "获得升级记录分页 tab 数量") + @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") + public CommonResult> getOtaUpgradeRecordCount( + @Valid IotOtaUpgradeRecordPageReqVO pageReqVO) { + return success(upgradeRecordService.getOtaUpgradeRecordCount(pageReqVO)); + } + @GetMapping("/page") @Operation(summary = "获得升级记录分页") @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") @@ -64,6 +64,7 @@ public class IotOtaUpgradeRecordController { return success(BeanUtils.toBean(upgradeRecord, IotOtaUpgradeRecordRespVO.class)); } + // TODO @li:使用 Putmapping @PostMapping("/retry") @Operation(summary = "重试升级记录") @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:retry')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java index 20216fecc7..4f560d5b5c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java @@ -44,6 +44,7 @@ public class IotOtaUpgradeTaskController { return success(true); } + // TODO @li:get 接口,不是 @RequestBody 哈 @GetMapping("/page") @Operation(summary = "获得升级任务分页") @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:query')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java index 01f10caa56..2799907cea 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java @@ -6,6 +6,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +// TODO @li:因为 create 和 update 可以公用的字段比较少,建议不用 IotOtaFirmwareCommonReqVO @Data @Schema(description = "管理后台 - OTA固件信息 Request VO") public class IotOtaFirmwareCommonReqVO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java index 44d942c633..b194cde776 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java @@ -7,10 +7,13 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -@Data +// TODO @li:中英文之间,有个空格。中文写作习惯哈。 @Schema(description = "管理后台 - OTA固件创建 Request VO") +@Data public class IotOtaFirmwareCreateReqVO extends IotOtaFirmwareCommonReqVO { + // TODO @li:因为有了注解,注释可以不写哈 + // TODO @li:swagger 注解,写在 validator 注解之前,保持项目统一哈。 /** * 版本号 */ @@ -27,6 +30,7 @@ public class IotOtaFirmwareCreateReqVO extends IotOtaFirmwareCommonReqVO { @Schema(description = "产品编号", requiredMode = REQUIRED, example = "1024") private String productId; + // TODO @li:productId 即可,而 productKey 通过 productId 查询 /** * 产品标识 *

@@ -44,6 +48,8 @@ public class IotOtaFirmwareCreateReqVO extends IotOtaFirmwareCommonReqVO { @Schema(description = "签名方式", example = "MD5") private String signMethod; + // TODO @li:fileSign、fileSize 通过后端下载文件,计算出来。对前端屏蔽这个细节。 + /** * 固件文件签名 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java index 8ae3e51994..24304202ca 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java @@ -5,7 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -@Schema(description = "管理后台 - OTA固件分页 Request VO") +@Schema(description = "管理后台 - OTA 固件分页 Request VO") public class IotOtaFirmwarePageReqVO extends PageParam { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java index 767ba77130..f9aa25cca7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java @@ -10,7 +10,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA固件 Response VO") +@Schema(description = "管理后台 - OTA 固件 Response VO") public class IotOtaFirmwareRespVO implements VO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java index c7c55e4cab..6e083a3323 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java @@ -11,6 +11,7 @@ import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Schema(description = "管理后台 - OTA升级记录分页 Request VO") public class IotOtaUpgradeRecordPageReqVO extends PageParam { + // TODO @li:使用 IotOtaUpgradeRecordStatusEnum 枚举哈 /** * 待处理状态 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java index c7da7565fc..7cb25299fb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java @@ -57,6 +57,7 @@ public class IotOtaUpgradeTaskSaveReqVO { @Schema(description = "选中的设备编号数组", requiredMode = REQUIRED, example = "[1,2,3,4]") private List deviceIds; + // TODO @li:通过 deviceIds 查询 deviceNames,前端不传递哈 /** * 选中的设备名字数组 *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java index 66ff07e3a2..2da9315f7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -16,6 +16,7 @@ public interface IotOtaUpgradeRecordConvert { IotOtaUpgradeRecordConvert INSTANCE = Mappers.getMapper(IotOtaUpgradeRecordConvert.class); + // TODO @li:一般情况下,这种 convert 直接写 service 就好啦。不用特别写一个哈 default List convertBOList(IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceList) { return deviceList.stream().map(device -> { IotOtaUpgradeRecordCreateReqBO createReqBO = new IotOtaUpgradeRecordCreateReqBO(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java index 1f300cfccb..e98e6c15c2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java @@ -70,6 +70,8 @@ public class IotOtaUpgradeTaskDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private List deviceIds; + + // TODO @li:这个通过查询,不用冗余 /** * 选中的设备名字数组 *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index e56efb8798..cd7d157a86 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -66,8 +66,7 @@ public interface IotDeviceMapper extends BaseMapperX { default Long selectCountByGroupId(Long groupId) { return selectCount(new LambdaQueryWrapperX() - .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0") - .orderByDesc(IotDeviceDO::getId)); + .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java index 36e7c61cdf..9bd4619164 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java @@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; +// TODO @li:这里的注释,可以去掉哈,多了点点 /** * IotOtaFirmwareMapper 接口用于操作 IotOtaFirmwareDO 实体类对应的数据库表。 * 该接口继承自 BaseMapperX,提供了基本的 CRUD 操作,并扩展了特定查询方法。 @@ -24,9 +25,8 @@ public interface IotOtaFirmwareMapper extends BaseMapperX { * @return 返回符合条件的固件信息列表。 */ default List selectByProductIdAndVersion(String productId, String version) { - return selectList(new LambdaQueryWrapperX() - .eq(IotOtaFirmwareDO::getProductId, productId) - .eq(IotOtaFirmwareDO::getVersion, version)); + return selectList(IotOtaFirmwareDO::getProductId, productId, + IotOtaFirmwareDO::getVersion, version); } /** @@ -36,11 +36,10 @@ public interface IotOtaFirmwareMapper extends BaseMapperX { * @return 返回分页查询结果,包含符合条件的固件信息列表。 */ default PageResult selectPage(IotOtaFirmwarePageReqVO pageReqVO) { - return selectPage(pageReqVO, - new LambdaQueryWrapperX() - .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName()) - .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId()) - .orderByDesc(IotOtaFirmwareDO::getCreateTime)); + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotOtaFirmwareDO::getName, pageReqVO.getName()) + .eqIfPresent(IotOtaFirmwareDO::getProductId, pageReqVO.getProductId()) + .orderByDesc(IotOtaFirmwareDO::getCreateTime)); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index 4f990fc212..35a164ca2c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -12,8 +12,9 @@ import org.apache.ibatis.annotations.Param; import java.util.List; +// TODO @li:这里的注释,可以去掉哈,多了点点 /** - * OTA升级记录 Mapper 接口 + * OTA 升级记录 Mapper 接口 */ @Mapper public interface IotOtaUpgradeRecordMapper extends BaseMapperX { @@ -78,6 +79,7 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX() .set(IotOtaUpgradeRecordDO::getStatus, IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()) .eq(IotOtaUpgradeRecordDO::getTaskId, taskId) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java index 9cee6e19a8..80c3ff5654 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java @@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; +// TODO @li:这里的注释,可以去掉哈,多了点点 /** * IotOtaUpgradeTaskMapper 接口用于操作 IotOtaUpgradeTaskDO 数据库表。 * 该接口继承自 BaseMapperX,提供了基本的数据库操作方法。 @@ -46,13 +47,11 @@ public interface IotOtaUpgradeTaskMapper extends BaseMapperX * 该函数通过传入的任务状态,查询数据库中符合条件的升级任务列表。 * - * @param state 任务状态,用于筛选升级任务的状态值 + * @param status 任务状态,用于筛选升级任务的状态值 * @return 返回符合条件的升级任务列表,列表中的每个元素为 IotOtaUpgradeTaskDO 对象 */ - default List selectUpgradeTaskByState(Integer state) { - // 使用 LambdaQueryWrapperX 构建查询条件,筛选出状态等于指定值的升级任务 - return selectList(new LambdaQueryWrapperX() - .eq(IotOtaUpgradeTaskDO::getStatus, state)); + default List selectUpgradeTaskByState(Integer status) { + return selectList(IotOtaUpgradeTaskDO::getStatus, status); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java index d04818d5c5..86c44db8b5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java @@ -19,13 +19,14 @@ public class IotOtaUpgradeRecordJob implements JobHandler { @Override @TenantJob public String execute(String param) throws Exception { - // 1.查询待处理的升级记录 + // 1. 查询待处理的升级记录 List upgradeRecords = upgradeRecordService .getUpgradeRecordListByState(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); // TODO @芋艿 2.执行升级动作 + // TODO @li:应该是逐条 push,逐条更新。不用批量哈 - // 3.最终,更新升级记录状态 + // 3. 最终,更新升级记录状态 List ids = upgradeRecords.stream().map(IotOtaUpgradeRecordDO::getId).toList(); upgradeRecordService.updateUpgradeRecordStatus(ids, IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); return ""; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java index 8b809c674b..5465161f98 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Component; import java.util.List; +// TODO @li:也不用通过 job 去统计。可以通过 record update status 后,主动去更新 task 的状态。 @Slf4j @Component public class IotOtaUpgradeTaskJob implements JobHandler { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java index 659b3039fc..67e14ce6a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import jakarta.validation.Valid; +// TODO @li:类、方法注释有点冗余,可以参考别的模块哈 /** * OTA固件管理服务接口 * 提供OTA固件的创建、更新和查询等功能 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java index 76930e1dc6..56a830b915 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java @@ -29,18 +29,22 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { @Override public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) { - // 1.校验固件产品id+版本号不能重复 + // 1. 校验固件产品 + 版本号不能重复 + // TODO @li:需要考虑设备也存在 validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion()); + // 2.转化数据格式,准备存储到数据库中 - IotOtaFirmwareDO otaFirmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); - otaFirmwareMapper.insert(otaFirmware); - return otaFirmware.getId(); + IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); + otaFirmwareMapper.insert(firmware); + return firmware.getId(); } @Override public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) { + // TODO @li:如果序号只有一个,直接写 1. 更好哈 // 1.1. 校验存在 validateFirmwareExists(updateReqVO.getId()); + // 2. 更新数据 IotOtaFirmwareDO updateObj = BeanUtils.toBean(updateReqVO, IotOtaFirmwareDO.class); otaFirmwareMapper.updateById(updateObj); @@ -58,11 +62,11 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { @Override public IotOtaFirmwareDO validateFirmwareExists(Long id) { - IotOtaFirmwareDO otaFirmware = otaFirmwareMapper.selectById(id); - if (otaFirmware == null) { + IotOtaFirmwareDO firmware = otaFirmwareMapper.selectById(id); + if (firmware == null) { throw exception(OTA_FIRMWARE_NOT_EXISTS); } - return otaFirmware; + return firmware; } /** @@ -80,6 +84,7 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { // 查询数据库中是否存在具有相同产品ID和版本号的固件信息 List list = otaFirmwareMapper.selectByProductIdAndVersion(productId, version); // 如果查询结果非空且不为null,则抛出异常,提示固件信息已存在 + // TODO @li:使用 isNotEmpty 这种 方法,简化 if (Objects.nonNull(list) && !list.isEmpty()) { throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java index 18bf63e83d..ea5de4a427 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.service.ota; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; -import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; -import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordUpdateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordUpdateReqBO; import jakarta.validation.Valid; import java.util.List; @@ -16,18 +16,21 @@ import java.util.Map; */ public interface IotOtaUpgradeRecordService { + // TODO @createOtaUpgradeRecordBatch 哈,需要补充方法里,缺少 Ota 关键字的 + /** * 批量创建物联网OTA升级记录 *

* 该函数用于处理一组物联网OTA升级记录的创建请求,并将这些记录批量保存到系统中。 * - * @param saveList 包含多个物联网OTA升级记录创建请求的列表,每个请求对象都经过校验(@Valid注解确保) + * @param createList 包含多个物联网OTA升级记录创建请求的列表,每个请求对象都经过校验(@Valid注解确保) * 列表中的每个元素都是IotOtaUpgradeRecordCreateReqBO类型的对象,表示一个独立的升级记录创建请求。 */ - void createUpgradeRecordBatch(@Valid List saveList); + void createUpgradeRecordBatch(@Valid List createList); + // TODO @li:尽量避免写比较大的通用 update。而是根据场景提供,这样才能收敛 /** - * 更新现有的OTA升级记录。 + * 更新现有的 OTA 升级记录 * * @param updateReqBO 包含更新升级记录所需信息的请求对象,必须经过验证。 */ @@ -36,14 +39,14 @@ public interface IotOtaUpgradeRecordService { /** * 获取OTA升级记录的数量统计。 * - * @return 返回一个Map,其中键为状态码,值为对应状态的升级记录数量。 + * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录数量 */ Map getOtaUpgradeRecordCount(@Valid IotOtaUpgradeRecordPageReqVO pageReqVO); /** - * 获取OTA升级记录的统计信息。 + * 获取 OTA 升级记录的统计信息。 * - * @return 返回一个Map,其中键为状态码,值为对应状态的升级记录统计信息。 + * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录统计信息 */ Map getOtaUpgradeRecordStatistics(Long firmwareId); @@ -68,8 +71,7 @@ public interface IotOtaUpgradeRecordService { * @param pageReqVO 包含分页查询条件的请求对象,必须经过验证。 * @return 返回包含分页查询结果的响应对象。 */ - PageResult getUpgradeRecordPage( - @Valid IotOtaUpgradeRecordPageReqVO pageReqVO); + PageResult getUpgradeRecordPage(@Valid IotOtaUpgradeRecordPageReqVO pageReqVO); /** * 根据任务ID取消升级记录。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java index 71168cd2d2..ae2b682739 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOta import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeRecordMapper; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; -import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordUpdateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordUpdateReqBO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -30,21 +30,22 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic private IotOtaUpgradeRecordMapper upgradeRecordMapper; @Override - public void createUpgradeRecordBatch(List saveList) { + public void createUpgradeRecordBatch(List createList) { // 1. 批量校验参数信息 - saveList.forEach(saveBO -> validateUpgradeRecordDuplicate(saveBO.getFirmwareId(), saveBO.getTaskId(), saveBO.getDeviceId())); - // 2.将数据转化成数据库存储的格式 - List upgradeRecords = BeanUtils.toBean(saveList, IotOtaUpgradeRecordDO.class); - // 3.将数据批量存储到数据库里 + createList.forEach(saveBO -> validateUpgradeRecordDuplicate(saveBO.getFirmwareId(), saveBO.getTaskId(), saveBO.getDeviceId())); + + // 2. 将数据批量存储到数据库里 + List upgradeRecords = BeanUtils.toBean(createList, IotOtaUpgradeRecordDO.class); upgradeRecordMapper.insertBatch(upgradeRecords); } @Override @Transactional public void updateUpgradeRecord(IotOtaUpgradeRecordUpdateReqBO updateReqBO) { - // 1.校验升级记录信息是否存在 + // 1. 校验升级记录信息是否存在 validateUpgradeRecordExists(updateReqBO.getId()); - // 2.将数据转化成数据库存储的格式 + + // 2. 将数据转化成数据库存储的格式 IotOtaUpgradeRecordDO updateRecord = BeanUtils.toBean(updateReqBO, IotOtaUpgradeRecordDO.class); upgradeRecordMapper.updateById(updateRecord); // TODO @芋艿: 更新升级记录触发的其他Action @@ -61,6 +62,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Transactional public Map getOtaUpgradeRecordCount(IotOtaUpgradeRecordPageReqVO pageReqVO) { // 分别查询不同状态的OTA升级记录数量 + // TODO @li: 通过 groupby 统计下; Long pending = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); Long pushed = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); Long upgrading = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()); @@ -68,6 +70,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic Long failure = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus()); Long canceled = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()); // 将各状态的数量封装到Map中返回 + // TODO @li:使用 MapUtil,因为 Map.of 是 jdk9 才有,后续不好同步到 master 的 jdk8; return Map.of(IotOtaUpgradeRecordPageReqVO.PENDING, pending, IotOtaUpgradeRecordPageReqVO.PUSHED, pushed, IotOtaUpgradeRecordPageReqVO.UPGRADING, upgrading, @@ -87,6 +90,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Transactional public Map getOtaUpgradeRecordStatistics(Long firmwareId) { // 查询并统计不同状态的OTA升级记录数量 + // TODO @li: 通过 groupby 统计下; Long pending = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); Long pushed = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); Long upgrading = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()); @@ -108,6 +112,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic IotOtaUpgradeRecordDO upgradeRecord = validateUpgradeRecordExists(id); // 1.2.校验升级记录是否可以重新升级 validateUpgradeRecordCanRetry(upgradeRecord); + // 2.将一些数据重置,这样定时任务轮询就可以重启任务 upgradeRecordMapper.updateById(new IotOtaUpgradeRecordDO() .setId(upgradeRecord.getId()).setProgress(0) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java index 5b3444214c..1f5476df15 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java @@ -8,6 +8,7 @@ import jakarta.validation.Valid; import java.util.List; +// TODO @li:类、方法注释有点冗余,可以参考别的模块哈 /** * IoT OTA升级任务服务接口 * 提供OTA升级任务的创建、取消和查询功能 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java index b66ae402c8..cef1448e9a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java @@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeTaskMapper; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record.IotOtaUpgradeRecordCreateReqBO; +import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; @@ -34,31 +34,37 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { @Resource private IotOtaUpgradeTaskMapper upgradeTaskMapper; - @Lazy + @Resource + @Lazy private IotDeviceService deviceService; - @Lazy @Resource + @Lazy private IotOtaFirmwareService firmwareService; - @Lazy @Resource + @Lazy private IotOtaUpgradeRecordService upgradeRecordService; @Override @Transactional(rollbackFor = Exception.class) public Long createUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO) { - // 1.1.校验同一固件的升级任务名称不重复 + // 1.1 校验同一固件的升级任务名称不重复 validateFirmwareTaskDuplicate(createReqVO.getFirmwareId(), createReqVO.getName()); - // 1.2.校验固件信息是否存在 + // 1.2 校验固件信息是否存在 IotOtaFirmwareDO firmware = firmwareService.validateFirmwareExists(createReqVO.getFirmwareId()); - // 1.3.校验升级范围=2(指定设备时),deviceIds deviceNames不为空并且长度相等 + // 1.3 校验升级范围=2(指定设备时),deviceIds deviceNames不为空并且长度相等 + // TODO @li:deviceNames 应该后端查询 validateScopeAndDevice(createReqVO.getScope(), createReqVO.getDeviceIds(), createReqVO.getDeviceNames()); - // 2.初始化OTA升级任务信息 + // TODO @li:如果全部范围,但是没设备可以升级,需要报错 + + // 2. 保存 OTA 升级任务信息到数据库 IotOtaUpgradeTaskDO upgradeTask = initUpgradeTask(createReqVO, firmware.getProductId()); - // 3.保存OTA升级任务信息到数据库 upgradeTaskMapper.insert(upgradeTask); - // 4.生成设备升级记录信息并存储,等待定时任务轮询 - List upgradeRecordList = initUpgradeRecordList(upgradeTask, firmware, createReqVO.getDeviceIds()); + + // 3. 生成设备升级记录信息并存储,等待定时任务轮询 + List upgradeRecordList = initUpgradeRecordList( + upgradeTask, firmware, createReqVO.getDeviceIds()); + // TODO @li:只需要传递 deviceIds、firewareId、剩余的 upgradeRecordService 里面自己处理;这样,后续 record 加字段,都不需要透传太多;解耦 upgradeRecordService.createUpgradeRecordBatch(upgradeRecordList); // TODO @芋艿: 创建任务触发的其他Action return upgradeTask.getId(); @@ -67,15 +73,17 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { @Override @Transactional(rollbackFor = Exception.class) public void cancelUpgradeTask(Long id) { - // 1.1.校验升级任务是否存在 + // 1.1 校验升级任务是否存在 IotOtaUpgradeTaskDO upgradeTask = validateUpgradeTaskExists(id); - // 1.2.校验升级任务是否可以取消 + // 1.2 校验升级任务是否可以取消 + // TODO @li:这种一次性的,可以不考虑拆分方法 validateUpgradeTaskCanCancel(upgradeTask); - // 2.更新OTA升级任务状态为已取消 + + // 2. 更新 OTA 升级任务状态为已取消 upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder() .id(id).status(IotOtaUpgradeTaskStatusEnum.CANCELED.getStatus()) .build()); - // 3.更新OTA升级记录状态为已取消 + // 3. 更新 OTA 升级记录状态为已取消 upgradeRecordService.cancelUpgradeRecordByTaskId(id); // TODO @芋艿: 取消任务触发的其他Action } @@ -176,6 +184,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { } } + // TODO @li:一次性,不复用的,可以直接写在对应的逻辑里; /** * 初始化升级任务 *

@@ -189,6 +198,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { private IotOtaUpgradeTaskDO initUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO, String productId) { // 配置各项参数 IotOtaUpgradeTaskDO upgradeTask = IotOtaUpgradeTaskDO.builder() + // TODO @li:不用每个占一行。最好相同类型的,放在一行里; .name(createReqVO.getName()) .description(createReqVO.getDescription()) .firmwareId(createReqVO.getFirmwareId()) @@ -219,7 +229,10 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { * @param deviceIds 设备ID列表,仅在升级任务范围为选择设备时使用 * @return 升级记录请求对象列表,包含每个设备的升级记录信息 */ - private List initUpgradeRecordList(IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceIds) { + private List initUpgradeRecordList( + IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceIds) { + // TODO @li:需要考虑,如果创建多个任务,相互之间不能重复; + // 1)指定设备的时候,进行校验;2)如果是全部,则过滤其它已经发起的;;;;;另外,需要排除掉 cancel 的哈。因为 cancal 之后,还可以发起 // 根据升级任务的范围确定设备列表 List deviceList; if (Objects.equals(upgradeTask.getScope(), IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java similarity index 96% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java index e3accc4275..2567be5294 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordCreateReqBO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record; +package cn.iocoder.yudao.module.iot.service.ota.bo; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java similarity index 93% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java index e0e71097c9..ac73aa884e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/record/IotOtaUpgradeRecordUpdateReqBO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.iot.service.ota.bo.upgrade.record; +package cn.iocoder.yudao.module.iot.service.ota.bo; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; @@ -11,6 +11,7 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +// TODO @li:这个类,貌似没用? @Data public class IotOtaUpgradeRecordUpdateReqBO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java deleted file mode 100644 index fbed390ae2..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota.bo; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java deleted file mode 100644 index af729fae30..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/upgrade/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota.bo.upgrade; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml index 0dac6efb5c..ea86ba5c40 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml @@ -4,6 +4,7 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + + + + + + + \ No newline at end of file From 006ef40c4b325b32e1aad54f55d7176e23554ad6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Wed, 26 Feb 2025 22:54:44 +0800 Subject: [PATCH 212/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=B7=BB=E5=8A=A0=20MQTT=20=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E9=9D=9E=E6=B3=95=E9=94=99=E8=AF=AF=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=AE=BE=E5=A4=87=E6=9C=8D=E5=8A=A1=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E5=92=8C=E5=B1=9E=E6=80=A7=E8=AE=BE=E7=BD=AE=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=20MQTT=20=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=A4=84=E7=90=86=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 3 + .../IotPluginEmqxAutoConfiguration.java | 37 +++- .../IotDeviceDownstreamHandlerImpl.java | 157 ++++++++++++- .../upstream/IotDeviceUpstreamServer.java | 195 ++++++++++------ .../router/IotDeviceMqttMessageHandler.java | 208 ++++++++++++------ 5 files changed, 450 insertions(+), 150 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 74fbb8b2e5..3d12c4b594 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -67,4 +67,7 @@ public interface ErrorCodeConstants { ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_007, "升级记录重复"); ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_008, "升级记录不能重试"); + // ========== MQTT 通信相关 1-050-009-000 ========== + ErrorCode MQTT_TOPIC_ILLEGAL = new ErrorCode(1_050_009_000, "topic illegal"); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java index d504e5704f..382bb9ecf2 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java @@ -1,9 +1,14 @@ package cn.iocoder.yudao.module.iot.plugin.emqx.config; +import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; import cn.iocoder.yudao.module.iot.plugin.emqx.downstream.IotDeviceDownstreamHandlerImpl; import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.IotDeviceUpstreamServer; +import io.vertx.core.Vertx; +import io.vertx.mqtt.MqttClient; +import io.vertx.mqtt.MqttClientOptions; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -13,19 +18,37 @@ import org.springframework.context.annotation.Configuration; * * @author haohao */ +@Slf4j @Configuration @EnableConfigurationProperties(IotPluginEmqxProperties.class) public class IotPluginEmqxAutoConfiguration { - @Bean(initMethod = "start", destroyMethod = "stop") - public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, - IotPluginEmqxProperties emqxProperties) { - return new IotDeviceUpstreamServer(emqxProperties, deviceUpstreamApi); + @Bean + public Vertx vertx() { + return Vertx.vertx(); } @Bean - public IotDeviceDownstreamHandler deviceDownstreamHandler() { - return new IotDeviceDownstreamHandlerImpl(); + public MqttClient mqttClient(Vertx vertx, IotPluginEmqxProperties emqxProperties) { + MqttClientOptions options = new MqttClientOptions() + .setClientId("yudao-iot-downstream-" + IdUtil.fastSimpleUUID()) + .setUsername(emqxProperties.getMqttUsername()) + .setPassword(emqxProperties.getMqttPassword()) + .setSsl(emqxProperties.isMqttSsl()); + return MqttClient.create(vertx, options); } -} + @Bean(initMethod = "start", destroyMethod = "stop") + public IotDeviceUpstreamServer deviceUpstreamServer(IotDeviceUpstreamApi deviceUpstreamApi, + IotPluginEmqxProperties emqxProperties, + Vertx vertx, + MqttClient mqttClient) { + return new IotDeviceUpstreamServer(emqxProperties, deviceUpstreamApi, vertx, mqttClient); + } + + @Bean + public IotDeviceDownstreamHandler deviceDownstreamHandler(MqttClient mqttClient) { + return new IotDeviceDownstreamHandlerImpl(mqttClient); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java index b1a8eebbf4..57445e5adb 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java @@ -1,8 +1,19 @@ package cn.iocoder.yudao.module.iot.plugin.emqx.downstream; +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.*; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import io.netty.handler.codec.mqtt.MqttQoS; +import io.vertx.core.buffer.Buffer; +import io.vertx.mqtt.MqttClient; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.MQTT_TOPIC_ILLEGAL; /** * EMQX 插件的 {@link IotDeviceDownstreamHandler} 实现类 @@ -10,14 +21,61 @@ import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamH * * @author 芋道源码 */ +@Slf4j public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { + private static final String SYS_TOPIC_PREFIX = "/sys/"; + + // 设备服务调用 标准 JSON + // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} + // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier}_reply + private static final String SERVICE_TOPIC_PREFIX = "/thing/service/"; + + // 设置设备属性 标准 JSON + // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set + // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply + private static final String PROPERTY_SET_TOPIC = "/thing/service/property/set"; + + private final MqttClient mqttClient; + + /** + * 构造函数 + * + * @param mqttClient MQTT客户端 + */ + public IotDeviceDownstreamHandlerImpl(MqttClient mqttClient) { + this.mqttClient = mqttClient; + } + @Override - public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { - // 设备服务调用 - // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} - // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier}_reply - return CommonResult.success(true); + public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO reqDTO) { + log.info("[invokeService][开始调用设备服务][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); + + // 验证参数 + if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null || reqDTO.getIdentifier() == null) { + log.error("[invokeService][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); + return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); + } + + try { + // 构建请求主题 + String topic = buildServiceTopic(reqDTO.getProductKey(), reqDTO.getDeviceName(), reqDTO.getIdentifier()); + + // 生成请求ID(如果没有提供) + String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); + + // 构建请求消息 + JSONObject request = buildServiceRequest(requestId, reqDTO.getIdentifier(), reqDTO.getParams()); + + // 发送消息 + publishMessage(topic, request); + + log.info("[invokeService][调用设备服务成功][requestId: {}][topic: {}]", requestId, topic); + return CommonResult.success(true); + } catch (Exception e) { + log.error("[invokeService][调用设备服务异常][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO), e); + return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); + } } @Override @@ -26,11 +84,34 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle } @Override - public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { - // 设置设备属性 标准 JSON - // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set - // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply - return CommonResult.success(true); + public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO reqDTO) { + log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); + + // 验证参数 + if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null) { + log.error("[setProperty][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); + return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); + } + + try { + // 构建请求主题 + String topic = buildPropertySetTopic(reqDTO.getProductKey(), reqDTO.getDeviceName()); + + // 生成请求ID(如果没有提供) + String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); + + // 构建请求消息 + JSONObject request = buildPropertySetRequest(requestId, reqDTO.getProperties()); + + // 发送消息 + publishMessage(topic, request); + + log.info("[setProperty][设置设备属性成功][requestId: {}][topic: {}]", requestId, topic); + return CommonResult.success(true); + } catch (Exception e) { + log.error("[setProperty][设置设备属性异常][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO), e); + return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); + } } @Override @@ -43,4 +124,60 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle return CommonResult.success(true); } + /** + * 构建服务调用主题 + */ + private String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) { + return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + SERVICE_TOPIC_PREFIX + serviceIdentifier; + } + + /** + * 构建属性设置主题 + */ + private String buildPropertySetTopic(String productKey, String deviceName) { + return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + PROPERTY_SET_TOPIC; + } + + /** + * 构建服务调用请求 + */ + private JSONObject buildServiceRequest(String requestId, String serviceIdentifier, Map params) { + return new JSONObject() + .set("id", requestId) + .set("version", "1.0") + .set("method", "thing.service." + serviceIdentifier) + .set("params", params != null ? params : new JSONObject()); + } + + /** + * 构建属性设置请求 + */ + private JSONObject buildPropertySetRequest(String requestId, Map properties) { + return new JSONObject() + .set("id", requestId) + .set("version", "1.0") + .set("method", "thing.service.property.set") + .set("params", properties); + } + + /** + * 发布MQTT消息 + */ + private void publishMessage(String topic, JSONObject payload) { + mqttClient.publish( + topic, + Buffer.buffer(payload.toString()), + MqttQoS.AT_LEAST_ONCE, + false, + false); + log.info("[publishMessage][发送消息成功][topic: {}][payload: {}]", topic, payload); + } + + /** + * 生成请求ID + */ + private String generateRequestId() { + return IdUtil.fastSimpleUUID(); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index baeddcb152..738860361e 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -1,19 +1,21 @@ package cn.iocoder.yudao.module.iot.plugin.emqx.upstream; -import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.emqx.config.IotPluginEmqxProperties; import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceAuthVertxHandler; import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceMqttMessageHandler; import io.netty.handler.codec.mqtt.MqttQoS; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.mqtt.MqttClient; -import io.vertx.mqtt.MqttClientOptions; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + /** * IoT 设备下行服务端,接收来自 device 设备的请求,转发给 server 服务器 *

@@ -24,20 +26,26 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class IotDeviceUpstreamServer { - private static final int RECONNECT_DELAY = 5000; // 重连延迟时间(毫秒) + private static final int RECONNECT_DELAY_MS = 5000; // 重连延迟时间(毫秒) + private static final int CONNECTION_TIMEOUT_MS = 10000; // 连接超时时间(毫秒) + private static final String TOPIC_SEPARATOR = ","; // 主题分隔符 + private static final MqttQoS DEFAULT_QOS = MqttQoS.AT_LEAST_ONCE; // 默认QoS级别 private final Vertx vertx; private final HttpServer server; private final MqttClient client; private final IotPluginEmqxProperties emqxProperties; private final IotDeviceMqttMessageHandler mqttMessageHandler; + private volatile boolean isRunning = false; // 服务运行状态标志 public IotDeviceUpstreamServer(IotPluginEmqxProperties emqxProperties, - IotDeviceUpstreamApi deviceUpstreamApi) { + IotDeviceUpstreamApi deviceUpstreamApi, + Vertx vertx, + MqttClient client) { + this.vertx = vertx; this.emqxProperties = emqxProperties; + this.client = client; - // 创建 Vertx 实例 - this.vertx = Vertx.vertx(); // 创建 Router 实例 Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); // 处理 Body @@ -45,14 +53,6 @@ public class IotDeviceUpstreamServer { .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); - - // 创建 MQTT 客户端 - MqttClientOptions options = new MqttClientOptions() - .setClientId("yudao-iot-server-" + IdUtil.fastSimpleUUID()) - .setUsername(emqxProperties.getMqttUsername()) - .setPassword(emqxProperties.getMqttPassword()) - .setSsl(emqxProperties.isMqttSsl()); - client = MqttClient.create(vertx, options); this.mqttMessageHandler = new IotDeviceMqttMessageHandler(deviceUpstreamApi, client); } @@ -60,25 +60,45 @@ public class IotDeviceUpstreamServer { * 启动 HTTP 服务器、MQTT 客户端 */ public void start() { + if (isRunning) { + log.warn("服务已经在运行中,请勿重复启动"); + return; + } + + log.info("[start] 开始启动服务"); + // 1. 启动 HTTP 服务器 - log.info("[start][开始启动]"); - server.listen(emqxProperties.getAuthPort()) + CompletableFuture httpFuture = server.listen(emqxProperties.getAuthPort()) .toCompletionStage() .toCompletableFuture() - .join(); - log.info("[start][HTTP服务器启动完成,端口({})]", this.server.actualPort()); + .thenAccept(v -> log.info("[start] HTTP服务器启动完成,端口: {}", server.actualPort())); // 2. 连接 MQTT Broker - connectMqtt(); + CompletableFuture mqttFuture = connectMqtt() + .toCompletionStage() + .toCompletableFuture() + .thenAccept(v -> { + // 3. 添加 MQTT 断开重连监听器 + client.closeHandler(closeEvent -> { + log.warn("[closeHandler] MQTT连接已断开,准备重连"); + reconnectWithDelay(); + }); - // 3. 添加 MQTT 断开重连监听器 - client.closeHandler(v -> { - log.warn("[closeHandler][MQTT 连接已断开,准备重连]"); - reconnectWithDelay(); - }); + // 4. 设置 MQTT 消息处理器 + setupMessageHandler(); + }); - // 4. 设置 MQTT 消息处理器 - setupMessageHandler(); + // 等待所有服务启动完成 + CompletableFuture.allOf(httpFuture, mqttFuture) + .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .whenComplete((result, error) -> { + if (error != null) { + log.error("[start] 服务启动失败", error); + } else { + isRunning = true; + log.info("[start] 所有服务启动完成"); + } + }); } /** @@ -86,79 +106,118 @@ public class IotDeviceUpstreamServer { */ private void setupMessageHandler() { client.publishHandler(mqttMessageHandler::handle); + log.debug("[setupMessageHandler] MQTT消息处理器设置完成"); } /** * 重连 MQTT 客户端 */ private void reconnectWithDelay() { - vertx.setTimer(RECONNECT_DELAY, id -> { - log.info("[reconnectWithDelay][开始重新连接 MQTT]"); + if (!isRunning) { + log.info("[reconnectWithDelay] 服务已停止,不再尝试重连"); + return; + } + + vertx.setTimer(RECONNECT_DELAY_MS, id -> { + log.info("[reconnectWithDelay] 开始重新连接MQTT"); connectMqtt(); }); } /** * 连接 MQTT Broker 并订阅主题 + * + * @return 连接结果的Future */ - private void connectMqtt() { - client.connect(emqxProperties.getMqttPort(), emqxProperties.getMqttHost()) - .onSuccess(connAck -> { - log.info("[connectMqtt][MQTT客户端连接成功]"); - subscribeToTopics(); + private Future connectMqtt() { + return client.connect(emqxProperties.getMqttPort(), emqxProperties.getMqttHost()) + .compose(connAck -> { + log.info("[connectMqtt] MQTT客户端连接成功"); + return subscribeToTopics(); }) - .onFailure(err -> { - log.error("[connectMqtt][连接 MQTT Broker 失败]", err); + .recover(err -> { + log.error("[connectMqtt] 连接MQTT Broker失败: {}", err.getMessage()); reconnectWithDelay(); + return Future.failedFuture(err); }); } /** * 订阅设备上行消息主题 + * + * @return 订阅结果的Future */ - private void subscribeToTopics() { - String[] topics = emqxProperties.getMqttTopics().split(","); - for (String topic : topics) { - client.subscribe(topic, MqttQoS.AT_LEAST_ONCE.value()) - .onSuccess(v -> log.info("[subscribeToTopics][成功订阅主题: {}]", topic)) - .onFailure(err -> log.error("[subscribeToTopics][订阅主题失败: {}]", topic, err)); + private Future subscribeToTopics() { + String topicsStr = emqxProperties.getMqttTopics(); + if (topicsStr == null || topicsStr.trim().isEmpty()) { + log.warn("[subscribeToTopics] 未配置MQTT主题,跳过订阅"); + return Future.succeededFuture(); } - log.info("[subscribeToTopics][开始订阅设备上行消息主题]"); + + log.info("[subscribeToTopics] 开始订阅设备上行消息主题"); + + String[] topics = topicsStr.split(TOPIC_SEPARATOR); + Future compositeFuture = Future.succeededFuture(); + + for (String topic : topics) { + String trimmedTopic = topic.trim(); + if (trimmedTopic.isEmpty()) { + continue; + } + + compositeFuture = compositeFuture.compose(v -> client.subscribe(trimmedTopic, DEFAULT_QOS.value()) + .map(ack -> { + log.info("[subscribeToTopics] 成功订阅主题: {}", trimmedTopic); + return null; + }) + .recover(err -> { + log.error("[subscribeToTopics] 订阅主题失败: {}, 原因: {}", + trimmedTopic, err.getMessage()); + return Future.succeededFuture(); // 继续订阅其他主题 + })); + } + + return compositeFuture; } /** - * 停止所有 + * 停止所有服务 */ public void stop() { - log.info("[stop][开始关闭]"); + if (!isRunning) { + log.warn("[stop] 服务未运行,无需停止"); + return; + } + + log.info("[stop] 开始关闭服务"); + isRunning = false; + try { - // 关闭 HTTP 服务器 - if (server != null) { - server.close() - .toCompletionStage() - .toCompletableFuture() - .join(); - } + CompletableFuture serverFuture = server != null + ? server.close().toCompletionStage().toCompletableFuture() + : CompletableFuture.completedFuture(null); - // 关闭 MQTT 客户端 - if (client != null) { - client.disconnect() - .toCompletionStage() - .toCompletableFuture() - .join(); - } + CompletableFuture clientFuture = client != null + ? client.disconnect().toCompletionStage().toCompletableFuture() + : CompletableFuture.completedFuture(null); - // 关闭 Vertx 实例 - if (vertx != null) { - vertx.close() - .toCompletionStage() - .toCompletableFuture() - .join(); - } - log.info("[stop][关闭完成]"); + CompletableFuture vertxFuture = vertx != null + ? vertx.close().toCompletionStage().toCompletableFuture() + : CompletableFuture.completedFuture(null); + + // 等待所有资源关闭 + CompletableFuture.allOf(serverFuture, clientFuture, vertxFuture) + .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .whenComplete((result, error) -> { + if (error != null) { + log.error("[stop] 服务关闭过程中发生异常", error); + } else { + log.info("[stop] 所有服务关闭完成"); + } + }); } catch (Exception e) { - log.error("[stop][关闭异常]", e); - throw new RuntimeException(e); + log.error("[stop] 关闭服务异常", e); + throw new RuntimeException("关闭IoT设备上行服务失败", e); } } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java index 6b99d781a4..83e9d1bfd3 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java @@ -13,13 +13,15 @@ import io.vertx.mqtt.messages.MqttPublishMessage; import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; +import java.util.Arrays; /** * IoT 设备 MQTT 消息处理器 *

* 参考: *

- * "..."> + * "..."> */ @Slf4j public class IotDeviceMqttMessageHandler { @@ -35,6 +37,12 @@ public class IotDeviceMqttMessageHandler { private static final String PROPERTY_POST_TOPIC = "/thing/event/property/post"; private static final String EVENT_POST_TOPIC_PREFIX = "/thing/event/"; private static final String EVENT_POST_TOPIC_SUFFIX = "/post"; + private static final String REPLY_SUFFIX = "_reply"; + private static final int SUCCESS_CODE = 200; + private static final String SUCCESS_MESSAGE = "success"; + private static final String PROPERTY_METHOD = "thing.event.property.post"; + private static final String EVENT_METHOD_PREFIX = "thing.event."; + private static final String EVENT_METHOD_SUFFIX = ".post"; private final IotDeviceUpstreamApi deviceUpstreamApi; private final MqttClient mqttClient; @@ -44,18 +52,33 @@ public class IotDeviceMqttMessageHandler { this.mqttClient = mqttClient; } + /** + * 处理MQTT消息 + * + * @param message MQTT发布消息 + */ public void handle(MqttPublishMessage message) { String topic = message.topicName(); String payload = message.payload().toString(); log.info("[messageHandler][接收到消息][topic: {}][payload: {}]", topic, payload); try { + if (payload == null || payload.isEmpty()) { + log.warn("[messageHandler][消息内容为空][topic: {}]", topic); + return; + } handleMessage(topic, payload); } catch (Exception e) { log.error("[messageHandler][处理消息失败][topic: {}][payload: {}]", topic, payload, e); } } + /** + * 根据主题类型处理消息 + * + * @param topic 主题 + * @param payload 消息内容 + */ private void handleMessage(String topic, String payload) { // 校验前缀 if (!topic.startsWith(SYS_TOPIC_PREFIX)) { @@ -88,34 +111,26 @@ public class IotDeviceMqttMessageHandler { * @param payload 消息内容 */ private void handlePropertyPost(String topic, String payload) { - // 解析消息内容 - JSONObject jsonObject = JSONUtil.parseObj(payload); - String[] topicParts = topic.split("/"); + try { + // 解析消息内容 + JSONObject jsonObject = JSONUtil.parseObj(payload); + String[] topicParts = parseTopic(topic); + if (topicParts == null) { + return; + } - // 构建设备属性上报请求对象 - IotDevicePropertyReportReqDTO reportReqDTO = buildPropertyReportDTO(jsonObject, topicParts); + // 构建设备属性上报请求对象 + IotDevicePropertyReportReqDTO reportReqDTO = buildPropertyReportDTO(jsonObject, topicParts); - // 调用上游 API 处理设备上报数据 - deviceUpstreamApi.reportDeviceProperty(reportReqDTO); - log.info("[handlePropertyPost][处理设备上行消息成功][topic: {}][reportReqDTO: {}]", - topic, JSONUtil.toJsonStr(reportReqDTO)); + // 调用上游 API 处理设备上报数据 + deviceUpstreamApi.reportDeviceProperty(reportReqDTO); + log.info("[handlePropertyPost][处理设备属性上报成功][topic: {}]", topic); - // 发送响应消息 - String replyTopic = topic + "_reply"; - JSONObject response = new JSONObject() - .set("id", jsonObject.getStr("id")) - .set("code", 200) - .set("data", new JSONObject()) - .set("message", "success") - .set("method", "thing.event.property.post"); - - mqttClient.publish(replyTopic, - Buffer.buffer(response.toString()), - MqttQoS.AT_LEAST_ONCE, - false, - false); - log.info("[handlePropertyPost][发送响应消息成功][topic: {}][response: {}]", - replyTopic, response.toString()); + // 发送响应消息 + sendResponse(topic, jsonObject, PROPERTY_METHOD, null); + } catch (Exception e) { + log.error("[handlePropertyPost][处理设备属性上报失败][topic: {}][payload: {}]", topic, payload, e); + } } /** @@ -125,35 +140,97 @@ public class IotDeviceMqttMessageHandler { * @param payload 消息内容 */ private void handleEventPost(String topic, String payload) { - // 解析消息内容 - JSONObject jsonObject = JSONUtil.parseObj(payload); + try { + // 解析消息内容 + JSONObject jsonObject = JSONUtil.parseObj(payload); + String[] topicParts = parseTopic(topic); + if (topicParts == null) { + return; + } + + // 构建设备事件上报请求对象 + IotDeviceEventReportReqDTO reportReqDTO = buildEventReportDTO(jsonObject, topicParts); + + // 调用上游 API 处理设备上报数据 + deviceUpstreamApi.reportDeviceEvent(reportReqDTO); + log.info("[handleEventPost][处理设备事件上报成功][topic: {}]", topic); + + // 从 topic 中获取事件标识符 + String eventIdentifier = getEventIdentifier(topicParts, topic); + if (eventIdentifier == null) { + return; + } + + // 发送响应消息 + String method = EVENT_METHOD_PREFIX + eventIdentifier + EVENT_METHOD_SUFFIX; + sendResponse(topic, jsonObject, method, null); + } catch (Exception e) { + log.error("[handleEventPost][处理设备事件上报失败][topic: {}][payload: {}]", topic, payload, e); + } + } + + /** + * 解析主题,获取主题各部分 + * + * @param topic 主题 + * @return 主题各部分数组,如果解析失败返回null + */ + private String[] parseTopic(String topic) { String[] topicParts = topic.split("/"); + if (topicParts.length < 7) { + log.warn("[parseTopic][主题格式不正确][topic: {}]", topic); + return null; + } + return topicParts; + } - // 构建设备事件上报请求对象 - IotDeviceEventReportReqDTO reportReqDTO = buildEventReportDTO(jsonObject, topicParts); + /** + * 从主题部分中获取事件标识符 + * + * @param topicParts 主题各部分 + * @param topic 原始主题,用于日志 + * @return 事件标识符,如果获取失败返回null + */ + private String getEventIdentifier(String[] topicParts, String topic) { + try { + return topicParts[6]; + } catch (ArrayIndexOutOfBoundsException e) { + log.warn("[getEventIdentifier][无法从主题中获取事件标识符][topic: {}][topicParts: {}]", + topic, Arrays.toString(topicParts)); + return null; + } + } - // 调用上游 API 处理设备上报数据 - deviceUpstreamApi.reportDeviceEvent(reportReqDTO); - log.info("[handleEventPost][处理设备上行消息成功][topic: {}][reportReqDTO: {}]", - topic, JSONUtil.toJsonStr(reportReqDTO)); + /** + * 发送响应消息 + * + * @param topic 原始主题 + * @param jsonObject 原始消息JSON对象 + * @param method 响应方法 + * @param customData 自定义数据,可为null + */ + private void sendResponse(String topic, JSONObject jsonObject, String method, JSONObject customData) { + String replyTopic = topic + REPLY_SUFFIX; + JSONObject data = customData != null ? customData : new JSONObject(); - // 发送响应消息 - String replyTopic = topic + "_reply"; - String eventIdentifier = topicParts[6]; // 从 topic 中获取事件标识符 JSONObject response = new JSONObject() .set("id", jsonObject.getStr("id")) - .set("code", 200) - .set("data", new JSONObject()) - .set("message", "success") - .set("method", "thing.event." + eventIdentifier + ".post"); + .set("code", SUCCESS_CODE) + .set("data", data) + .set("message", SUCCESS_MESSAGE) + .set("method", method); - mqttClient.publish(replyTopic, - Buffer.buffer(response.toString()), - MqttQoS.AT_LEAST_ONCE, - false, - false); - log.info("[handleEventPost][发送响应消息成功][topic: {}][response: {}]", - replyTopic, response.toString()); + try { + mqttClient.publish(replyTopic, + Buffer.buffer(response.toString()), + MqttQoS.AT_LEAST_ONCE, + false, + false); + log.info("[sendResponse][发送响应消息成功][topic: {}]", replyTopic); + } catch (Exception e) { + log.error("[sendResponse][发送响应消息失败][topic: {}][response: {}]", + replyTopic, response.toString(), e); + } } /** @@ -163,15 +240,15 @@ public class IotDeviceMqttMessageHandler { * @param topicParts 主题部分 * @return 设备属性上报请求对象 */ - private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, - String[] topicParts) { - return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO() - .setRequestId(jsonObject.getStr("id")) - .setProcessId(IotPluginCommonUtils.getProcessId()) - .setReportTime(LocalDateTime.now()) - .setProductKey(topicParts[2]) - .setDeviceName(topicParts[3])) - .setProperties(jsonObject.getJSONObject("params")); + private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, String[] topicParts) { + IotDevicePropertyReportReqDTO reportReqDTO = new IotDevicePropertyReportReqDTO(); + reportReqDTO.setRequestId(jsonObject.getStr("id")); + reportReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); + reportReqDTO.setReportTime(LocalDateTime.now()); + reportReqDTO.setProductKey(topicParts[2]); + reportReqDTO.setDeviceName(topicParts[3]); + reportReqDTO.setProperties(jsonObject.getJSONObject("params")); + return reportReqDTO; } /** @@ -182,13 +259,14 @@ public class IotDeviceMqttMessageHandler { * @return 设备事件上报请求对象 */ private IotDeviceEventReportReqDTO buildEventReportDTO(JSONObject jsonObject, String[] topicParts) { - return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO() - .setRequestId(jsonObject.getStr("id")) - .setProcessId(IotPluginCommonUtils.getProcessId()) - .setReportTime(LocalDateTime.now()) - .setProductKey(topicParts[2]) - .setDeviceName(topicParts[3])) - .setIdentifier(topicParts[4]) - .setParams(jsonObject.getJSONObject("params")); + IotDeviceEventReportReqDTO reportReqDTO = new IotDeviceEventReportReqDTO(); + reportReqDTO.setRequestId(jsonObject.getStr("id")); + reportReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); + reportReqDTO.setReportTime(LocalDateTime.now()); + reportReqDTO.setProductKey(topicParts[2]); + reportReqDTO.setDeviceName(topicParts[3]); + reportReqDTO.setIdentifier(topicParts[6]); + reportReqDTO.setParams(jsonObject.getJSONObject("params")); + return reportReqDTO; } } \ No newline at end of file From 6cf7a67406929df0e4cd9c57e844c4ce8f777269 Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Thu, 27 Feb 2025 10:52:28 +0800 Subject: [PATCH 213/309] =?UTF-8?q?[fix]=EF=BC=9Aiot=20home=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/IotStatisticsController.java | 12 +++++++----- .../admin/statistics/vo/IotStatisticsReqVO.java | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index 9752a04ab7..61f773f472 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsReqVO; import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; @@ -10,6 +11,7 @@ import cn.iocoder.yudao.module.iot.service.product.IotProductService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,9 +39,9 @@ public class IotStatisticsController { private IotDeviceLogService iotDeviceLogService; - @GetMapping("/count") + @GetMapping("/main") @Operation(summary = "获取IOT首页的数据统计", description = "用于IOT首页的数据统计") - public CommonResult getIotCount(){ + public CommonResult getIotMainStats(@Valid IotStatisticsReqVO reqVO){ IotStatisticsRespVO iotStatisticsRespVO = new IotStatisticsRespVO(); // 获取总数 iotStatisticsRespVO.setCategoryTotal(iotProductCategoryService.getProductCategoryCount(null)); @@ -64,9 +66,9 @@ public class IotStatisticsController { iotStatisticsRespVO.setOfflineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.OFFLINE.getState())); iotStatisticsRespVO.setNeverOnlineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.INACTIVE.getState())); - // 获取设备上下行消息数量统计 - iotStatisticsRespVO.setDeviceUpMessageStats(iotDeviceLogService.getDeviceLogUpCountByHour(null,null,null)); - iotStatisticsRespVO.setDeviceDownMessageStats(iotDeviceLogService.getDeviceLogDownCountByHour(null,null,null)); + // 根据传入时间范围获取设备上下行消息数量统计 + iotStatisticsRespVO.setDeviceUpMessageStats(iotDeviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); + iotStatisticsRespVO.setDeviceDownMessageStats(iotDeviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); return CommonResult.success(iotStatisticsRespVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java new file mode 100644 index 0000000000..56f013963b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - Iot统计 Request VO") +@Data +public class IotStatisticsReqVO { + @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @NotNull(message = "查询起始时间不能为空") + Long startTime; + @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @NotNull(message = "查询结束时间不能为空") + Long endTime; +} From 36dd18d41faa4a34a197821e73af20116ba97da1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 27 Feb 2025 12:45:42 +0800 Subject: [PATCH 214/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AMQTT=20=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/util/MqttSignUtils.java | 2 ++ .../emqx/config/IotPluginEmqxProperties.java | 1 + .../IotDeviceDownstreamHandlerImpl.java | 1 + .../upstream/IotDeviceUpstreamServer.java | 27 +++++++++++++++---- .../router/IotDeviceAuthVertxHandler.java | 4 ++- .../router/IotDeviceMqttMessageHandler.java | 10 ++++--- 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java index 7a6b6e441a..01a6dba932 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/util/MqttSignUtils.java @@ -59,9 +59,11 @@ public class MqttSignUtils { @Getter @AllArgsConstructor public static class MqttSignResult { + private final String clientId; private final String username; private final String password; + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java index fbc689b353..72a085bd9d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java @@ -36,6 +36,7 @@ public class IotPluginEmqxProperties { */ private boolean mqttSsl; + // TODO @haohao:这个是不是改成数组? /** * 订阅的主题 */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java index 57445e5adb..aed677c49e 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java @@ -26,6 +26,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle private static final String SYS_TOPIC_PREFIX = "/sys/"; + // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。 // 设备服务调用 标准 JSON // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier}_reply diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index 738860361e..5dd627671f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -26,17 +26,33 @@ import java.util.concurrent.TimeUnit; @Slf4j public class IotDeviceUpstreamServer { - private static final int RECONNECT_DELAY_MS = 5000; // 重连延迟时间(毫秒) - private static final int CONNECTION_TIMEOUT_MS = 10000; // 连接超时时间(毫秒) - private static final String TOPIC_SEPARATOR = ","; // 主题分隔符 - private static final MqttQoS DEFAULT_QOS = MqttQoS.AT_LEAST_ONCE; // 默认QoS级别 + /** + * 重连延迟时间(毫秒) + */ + private static final int RECONNECT_DELAY_MS = 5000; + /** + * 连接超时时间(毫秒) + */ + private static final int CONNECTION_TIMEOUT_MS = 10000; + /** + * 主题分隔符 + */ + private static final String TOPIC_SEPARATOR = ","; + /** + * 默认 QoS 级别 + */ + private static final MqttQoS DEFAULT_QOS = MqttQoS.AT_LEAST_ONCE; private final Vertx vertx; private final HttpServer server; private final MqttClient client; private final IotPluginEmqxProperties emqxProperties; private final IotDeviceMqttMessageHandler mqttMessageHandler; - private volatile boolean isRunning = false; // 服务运行状态标志 + + /** + * 服务运行状态标志 + */ + private volatile boolean isRunning = false; public IotDeviceUpstreamServer(IotPluginEmqxProperties emqxProperties, IotDeviceUpstreamApi deviceUpstreamApi, @@ -50,6 +66,7 @@ public class IotDeviceUpstreamServer { Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); // 处理 Body router.post(IotDeviceAuthVertxHandler.PATH) + // TODO @haohao:疑问,mqtt 的认证,需要通过 http 呀? .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java index 794949c287..8eac1ffbde 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -27,7 +27,7 @@ public class IotDeviceAuthVertxHandler implements Handler { @Override @SuppressWarnings("unchecked") public void handle(RoutingContext routingContext) { - + // TODO @haohao:try catch 兜底异常 JsonObject json = routingContext.body().asJsonObject(); String clientId = json.getString("clientid"); String username = json.getString("username"); @@ -40,9 +40,11 @@ public class IotDeviceAuthVertxHandler implements Handler { denyAccess(routingContext); return; } + // TODO @haohao:貌似可以考虑封装一个 writeJson ,里面有个参数是 data,然后里面去 JsonUtils.toJsonString(data) IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"allow\"}"); } + // TODO @haohao:下面两个简单方法,貌似可以考虑不抽小方法哈。 private void denyAccess(RoutingContext routingContext) { IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"deny\"}"); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java index 83e9d1bfd3..9851d1747f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java @@ -26,12 +26,14 @@ import java.util.Arrays; @Slf4j public class IotDeviceMqttMessageHandler { + // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。 // 设备上报属性 标准 JSON - // 请求Topic:/sys/${productKey}/${deviceName}/thing/event/property/post - // 响应Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply + // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post + // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply + // 设备上报事件 标准 JSON - // 请求Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post - // 响应Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply + // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post + // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply private static final String SYS_TOPIC_PREFIX = "/sys/"; private static final String PROPERTY_POST_TOPIC = "/thing/event/property/post"; From 6e1ec8b3eb7ee3261a58126c86ff2c516a1b78cd Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 27 Feb 2025 13:30:39 +0800 Subject: [PATCH 215/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E9=A6=96=E9=A1=B5=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/IotStatisticsController.java | 46 +++++++++---------- .../statistics/vo/IotStatisticsReqVO.java | 6 +++ .../statistics/vo/IotStatisticsRespVO.java | 31 +++++++++++-- .../iot/dal/mysql/device/IotDeviceMapper.java | 17 ++----- .../product/IotProductCategoryMapper.java | 10 +--- .../dal/mysql/product/IotProductMapper.java | 15 +----- .../mysql/thingmodel/IotThingModelMapper.java | 1 + .../iot/dal/tdengine/IotDeviceLogMapper.java | 3 +- .../iot/service/device/IotDeviceService.java | 2 +- .../service/device/IotDeviceServiceImpl.java | 1 + .../device/data/IotDeviceLogService.java | 12 +++-- .../device/data/IotDeviceLogServiceImpl.java | 2 + .../product/IotProductCategoryService.java | 4 +- .../IotProductCategoryServiceImpl.java | 9 ++-- .../service/product/IotProductService.java | 4 +- .../thingmodel/IotThingModelService.java | 1 + .../thingmodel/IotThingModelServiceImpl.java | 1 + 17 files changed, 90 insertions(+), 75 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index 61f773f472..bb5fa92ae8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -19,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; - @Tag(name = "管理后台 - IoT 数据统计") @RestController @RequestMapping("/iot/statistics") @@ -27,48 +26,47 @@ import java.time.LocalDateTime; public class IotStatisticsController { @Resource - private IotDeviceService iotDeviceService; - + private IotDeviceService deviceService; @Resource - private IotProductCategoryService iotProductCategoryService; - + private IotProductCategoryService productCategoryService; @Resource - private IotProductService iotProductService; - + private IotProductService productService; @Resource - private IotDeviceLogService iotDeviceLogService; - + private IotDeviceLogService deviceLogService; + // TODO @super:description 非必要,可以不写哈 @GetMapping("/main") - @Operation(summary = "获取IOT首页的数据统计", description = "用于IOT首页的数据统计") + @Operation(summary = "获取首页的数据统计", description = "用于IOT首页的数据统计") public CommonResult getIotMainStats(@Valid IotStatisticsReqVO reqVO){ + // TODO @super:新增 get-summary 接口,返回:总数、今日新增、数量、状态 IotStatisticsRespVO iotStatisticsRespVO = new IotStatisticsRespVO(); // 获取总数 - iotStatisticsRespVO.setCategoryTotal(iotProductCategoryService.getProductCategoryCount(null)); - iotStatisticsRespVO.setProductTotal(iotProductService.getProductCount(null)); - iotStatisticsRespVO.setDeviceTotal(iotDeviceService.getDeviceCount(null)); - iotStatisticsRespVO.setReportTotal(iotDeviceLogService.getDeviceLogCount(null)); + iotStatisticsRespVO.setCategoryTotal(productCategoryService.getProductCategoryCount(null)); + iotStatisticsRespVO.setProductTotal(productService.getProductCount(null)); + iotStatisticsRespVO.setDeviceTotal(deviceService.getDeviceCount(null)); + iotStatisticsRespVO.setReportTotal(deviceLogService.getDeviceLogCount(null)); // 获取今日新增数量 LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); - iotStatisticsRespVO.setCategoryTodayTotal(iotProductCategoryService.getProductCategoryCount(todayStart)); - iotStatisticsRespVO.setProductTodayTotal(iotProductService.getProductCount(todayStart)); - iotStatisticsRespVO.setDeviceTodayTotal(iotDeviceService.getDeviceCount(todayStart)); - iotStatisticsRespVO.setReportTodayTotal(iotDeviceLogService.getDeviceLogCount(todayStart)); + iotStatisticsRespVO.setCategoryTodayTotal(productCategoryService.getProductCategoryCount(todayStart)); + iotStatisticsRespVO.setProductTodayTotal(productService.getProductCount(todayStart)); + iotStatisticsRespVO.setDeviceTodayTotal(deviceService.getDeviceCount(todayStart)); + iotStatisticsRespVO.setReportTodayTotal(deviceLogService.getDeviceLogCount(todayStart)); // 获取各个品类下设备数量统计 iotStatisticsRespVO.setDeviceStatsOfCategory( - iotProductCategoryService.getDeviceCountsOfProductCategory() + productCategoryService.getDeviceCountsOfProductCategory() ); // 获取设备状态数量统计 - iotStatisticsRespVO.setOnlineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.ONLINE.getState())); - iotStatisticsRespVO.setOfflineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.OFFLINE.getState())); - iotStatisticsRespVO.setNeverOnlineTotal(iotDeviceService.getDeviceCountByState(IotDeviceStateEnum.INACTIVE.getState())); + iotStatisticsRespVO.setOnlineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.ONLINE.getState())); + iotStatisticsRespVO.setOfflineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.OFFLINE.getState())); + iotStatisticsRespVO.setNeverOnlineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.INACTIVE.getState())); + // TODO @super:新增 get-log-summary 接口,返回 // 根据传入时间范围获取设备上下行消息数量统计 - iotStatisticsRespVO.setDeviceUpMessageStats(iotDeviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); - iotStatisticsRespVO.setDeviceDownMessageStats(iotDeviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); + iotStatisticsRespVO.setDeviceUpMessageStats(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); + iotStatisticsRespVO.setDeviceDownMessageStats(deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); return CommonResult.success(iotStatisticsRespVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java index 56f013963b..4a225bc5a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java @@ -7,10 +7,16 @@ import lombok.Data; @Schema(description = "管理后台 - Iot统计 Request VO") @Data public class IotStatisticsReqVO { + + // TODO @supper:times 直接传递哈; + // TODO 2super:private 不要丢了 + @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") @NotNull(message = "查询起始时间不能为空") Long startTime; + @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") @NotNull(message = "查询结束时间不能为空") Long endTime; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java index a72c0536e5..0e68c078d0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java @@ -5,10 +5,16 @@ import lombok.Data; import java.util.List; -@Schema(description = "管理后台 - Iot统计 Response VO") +// TODO @super:Total 全部改成 Count +// TODO @super:IotStatisticsSummaryRespVO +/** + * 管理后台 - Iot 统计 Response VO + */ +@Schema(description = "管理后台 - Iot 统计 Response VO") @Data public class IotStatisticsRespVO { + // TODO @super:productCategory 哈 @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private long categoryTotal; @@ -18,9 +24,11 @@ public class IotStatisticsRespVO { @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") private long deviceTotal; + // TODO @super:deviceMessageCount;设备消息数量 @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") private long reportTotal; + // TODO @super:productCategory 哈 @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private long categoryTodayTotal; @@ -30,30 +38,42 @@ public class IotStatisticsRespVO { @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") private long deviceTodayTotal; + // TODO @super:deviceMessageCount;今日设备消息数量 @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") private long reportTodayTotal; + // TODO @super:deviceOnlineCount + @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") private long onlineTotal; + // TODO @super:deviceOfflineCount + @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") private long offlineTotal; + // TODO @super:deviceInactivECount + @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") private long neverOnlineTotal; + // TODO @super:1)类型改成 Map,key 分类名、value 设备数量;2)deviceStatsOfCategory => productCategoryDeviceCounts + @Schema(description = "按品类统计的设备数量") + private List deviceStatsOfCategory; + + // TODO @super:貌似界面里,用不到这个字段??? @Schema(description = "上报数据数量统计") private List reportDataStats; + // TODO @super:deviceUpMessageStats、deviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO,然后里面属性就是 upstreamCounts、downstreamCounts + @Schema(description = "上行数据数量统计") private List deviceUpMessageStats; @Schema(description = "下行数据数量统计") private List deviceDownMessageStats; - @Schema(description = "按品类统计的设备数量") - private List deviceStatsOfCategory; - + // TODO @super:如果只有这两个字段,使用 KeyValue 这个键值对 @Schema(description = "时间数据") @Data public static class TimeData { @@ -63,6 +83,7 @@ public class IotStatisticsRespVO { @Schema(description = "数据值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") private Object data; + } @Schema(description = "数据项") @@ -74,5 +95,7 @@ public class IotStatisticsRespVO { @Schema(description = "数据项值", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") private Object value; + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 0f642541ef..cceeb53385 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; +import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; @@ -70,24 +71,12 @@ public interface IotDeviceMapper extends BaseMapperX { .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")); } - /** - * 统计设备数量 - * - * @param createTime 创建时间,如果为空,则统计所有设备数量 - * @return 设备数量 - */ - default Long selectCountByCreateTime(LocalDateTime createTime) { + default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { return selectCount(new LambdaQueryWrapperX() .geIfPresent(IotDeviceDO::getCreateTime, createTime)); } - /** - * 统计指定状态的设备数量 - * - * @param state 状态 - * @return 设备数量 - */ - default Long selectCountByState(Integer state) { + default Long selectCountByState(@Nullable Integer state) { return selectCount(new LambdaQueryWrapperX() .eqIfPresent(IotDeviceDO::getState, state)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java index 6e9deb4eba..dc9367bbd4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; +import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; @@ -30,13 +30,7 @@ public interface IotProductCategoryMapper extends BaseMapperX() .geIfPresent(IotProductCategoryDO::getCreateTime, createTime)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index 11a2551610..f63a14be95 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; +import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; @@ -31,23 +32,11 @@ public interface IotProductMapper extends BaseMapperX { .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); } - /** - * 统计产品数量 - * - * @param createTime 创建时间,如果为空,则统计所有产品数量 - * @return 产品数量 - */ - default Long selectCountByCreateTime(LocalDateTime createTime) { + default Long selectCountByCreateTime(@Nullable LocalDateTime createTime) { return selectCount(new LambdaQueryWrapperX() .geIfPresent(IotProductDO::getCreateTime, createTime)); } - /** - * 获得产品列表,基于分类编号 - * - * @param categoryId 分类编号 - * @return 产品列表 - */ default List selectListByCategoryId(Long categoryId) { return selectList(new LambdaQueryWrapperX() .eq(IotProductDO::getCategoryId, categoryId) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java index 21972343fc..082386b4e0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/thingmodel/IotThingModelMapper.java @@ -73,6 +73,7 @@ public interface IotThingModelMapper extends BaseMapperX { IotThingModelDO::getName, name); } + // TODO @super:用不到,删除下; /** * 统计物模型数量 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java index ab351e158e..df64aefb30 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java @@ -9,7 +9,6 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; -import java.time.LocalDateTime; import java.util.List; /** @@ -58,6 +57,8 @@ public interface IotDeviceLogMapper { */ Long selectCountByCreateTime(@Param("createTime") Long createTime); + // TODO @super:1)上行、下行,不写在 mapper 里,而是通过参数传递,这样,selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并; + // TODO @super:2)不能只基于 identifier 来计算,而是要 type + identifier 成对 /** * 获得每个小时设备上行消息数量统计 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 151dc6266c..6d48b38d29 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -192,7 +192,7 @@ public interface IotDeviceService { * @param createTime 创建时间,如果为空,则统计所有设备数量 * @return 设备数量 */ - Long getDeviceCount(LocalDateTime createTime); + Long getDeviceCount(@Nullable LocalDateTime createTime); /** * 获得设备数量,基于状态 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 28925cd9dd..4d4892733b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -429,6 +429,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectCountByCreateTime(createTime); } + // TODO @super:是不是 groupby 查询,更高效;不过 controller,还是要考虑 null 的情况;不过可以直接枚举 foreach 处理下 @Override public Long getDeviceCountByState(Integer state) { return deviceMapper.selectCountByState(state); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java index faff5a928f..592217bb60 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java @@ -5,8 +5,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogP import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; -import org.apache.ibatis.annotations.Param; +import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; @@ -45,7 +45,7 @@ public interface IotDeviceLogService { * @param createTime 创建时间,如果为空,则统计所有日志数量 * @return 日志数量 */ - Long getDeviceLogCount(LocalDateTime createTime); + Long getDeviceLogCount(@Nullable LocalDateTime createTime); /** * 获得每个小时设备上行消息数量统计 @@ -55,7 +55,9 @@ public interface IotDeviceLogService { * @param endTime 结束时间,如果为空,则不限制结束时间 * @return 每小时消息数量统计列表 */ - List getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime); + List getDeviceLogUpCountByHour(@Nullable String deviceKey, + @Nullable Long startTime, + @Nullable Long endTime); /** * 获得每个小时设备下行消息数量统计 @@ -65,6 +67,8 @@ public interface IotDeviceLogService { * @param endTime 结束时间,如果为空,则不限制结束时间 * @return 每小时消息数量统计列表 */ - List getDeviceLogDownCountByHour( String deviceKey, Long startTime, Long endTime); + List getDeviceLogDownCountByHour(@Nullable String deviceKey, + @Nullable Long startTime, + @Nullable Long endTime); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java index ea3a8ac5f9..a5ac4a8c1b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -73,11 +73,13 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { public Long getDeviceLogCount(LocalDateTime createTime) { Long time = null; if (createTime != null) { + // todo @super:1)LocalDateTimeUtil.toEpochMilli(createTime);2)直接表达式,更简洁 time != null ? createTime.toInstant(ZoneOffset.UTC).toEpochMilli() : null; time = createTime.toInstant(ZoneOffset.UTC).toEpochMilli(); } return deviceLogMapper.selectCountByCreateTime(time); } + // TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤 @Override public List getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { try { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java index b4e8fa3f95..8d6ccdadaa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsR import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import jakarta.validation.Valid; +import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; @@ -91,8 +92,9 @@ public interface IotProductCategoryService { * @param createTime 创建时间,如果为空,则统计所有分类数量 * @return 产品分类数量 */ - Long getProductCategoryCount(LocalDateTime createTime); + Long getProductCategoryCount(@Nullable LocalDateTime createTime); + // TODO @super:1)Map 虽然有点怪哈,然后 Controller 按需转换成 Map ;2)名字 getProductCategoryDeviceCountMap 方法 /** * 获得各个品类下设备数量统计 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index c4af16c9df..d8b9af1fb3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -11,7 +11,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -108,16 +107,18 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService // 1. 获取所有数据 List categoryList = productCategoryMapper.selectList(); List productList = productService.getProductList(); + // TODO @super:不要 list 查询,返回内存,而是查询一个 Map List deviceList = deviceService.getDeviceList(); // 2. 统计每个分类下的设备数量 Map categoryDeviceCountMap = new HashMap<>(); - + // 2.1 初始化所有分类的计数为0 for (IotProductCategoryDO category : categoryList) { categoryDeviceCountMap.put(category.getName(), 0); + // TODO @super:直接这里面计算,不用多个循环。产品本身也不多,不用构建 Map,直接 filter 就好了 } - + // 2.2 构建产品ID到分类的映射 Map productCategoryMap = new HashMap<>(); for (IotProductDO product : productList) { @@ -130,7 +131,7 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService productCategoryMap.put(product.getId(), category); } } - + // 2.3 统计每个分类下的设备数量 for (IotDeviceDO device : deviceList) { Long productId = device.getProductId(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index 590db15a8d..5d6f7c788a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProduc import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import jakarta.validation.Valid; +import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; @@ -99,8 +100,9 @@ public interface IotProductService { * @param createTime 创建时间,如果为空,则统计所有产品数量 * @return 产品数量 */ - Long getProductCount(LocalDateTime createTime); + Long getProductCount(@Nullable LocalDateTime createTime); + // TODO @super:用不到的,删除下哈 /** * 获得产品列表,基于分类编号 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java index 6eb8bb346d..8834772d35 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java @@ -81,6 +81,7 @@ public interface IotThingModelService { */ List getThingModelList(IotThingModelListReqVO reqVO); + // TODO @super:用不到,删除下哈。 /** * 获得物模型数量 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java index 813b5ea585..9487ff2de6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java @@ -364,6 +364,7 @@ public class IotThingModelServiceImpl implements IotThingModelService { return SpringUtil.getBean(getClass()); } + // TODO @super:用不到,删除下; @Override public Long getThingModelCount(LocalDateTime createTime) { return thingModelMapper.selectCountByCreateTime(createTime); From 0302ebee9915bc6abb3435c11df47dff57b9bbc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=8E=84=E7=A4=BC?= Date: Thu, 27 Feb 2025 23:07:09 +0800 Subject: [PATCH 216/309] =?UTF-8?q?refactor(iot):=20=E4=BC=98=E5=8C=96=20O?= =?UTF-8?q?TA=20=E5=8D=87=E7=BA=A7=E8=AE=B0=E5=BD=95=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重写 getOtaUpgradeRecordCount 和 getOtaUpgradeRecordStatistics 方法,使用 MyBatis-Plus 的 LambdaQueryWrapperX -移除 XML 中对应的 SQL 查询语句 - 提高代码可维护性和数据库兼容性 --- .../mysql/ota/IotOtaUpgradeRecordMapper.java | 20 ++++++++++++++----- .../mapper/ota/IotOtaUpgradeRecordMapper.xml | 17 ---------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index 35a164ca2c..83bdb43065 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -13,6 +13,7 @@ import org.apache.ibatis.annotations.Param; import java.util.List; // TODO @li:这里的注释,可以去掉哈,多了点点 + /** * OTA 升级记录 Mapper 接口 */ @@ -43,9 +44,14 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX() + .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, taskId) + .likeIfPresent(IotOtaUpgradeRecordDO::getDeviceId, deviceName) + .eqIfPresent(IotOtaUpgradeRecordDO::getStatus, status)); + } /** * 获取OTA升级记录的统计信息 @@ -54,8 +60,12 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX() + .eqIfPresent(IotOtaUpgradeRecordDO::getFirmwareId, firmwareId) + .eqIfPresent(IotOtaUpgradeRecordDO::getStatus, status)); + } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml index ea86ba5c40..74fa85eaca 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml @@ -4,21 +4,4 @@ "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> - - - - - - \ No newline at end of file From 4629084c1ba1411109403dcec1f11b6096b46f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=8E=84=E7=A4=BC?= Date: Thu, 27 Feb 2025 23:18:25 +0800 Subject: [PATCH 217/309] =?UTF-8?q?refactor(iot):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E8=AE=B0=E5=BD=95=E7=8A=B6=E6=80=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 IotOtaUpgradeRecordMapper 中的 cancelUpgradeRecordByTaskId 方法- 新增 updateUpgradeRecordStatusByTaskIdAndStatus 方法,用于更通用的状态更新 - 在 IotOtaUpgradeRecordServiceImpl 中调用新方法来取消升级记录 --- .../mysql/ota/IotOtaUpgradeRecordMapper.java | 19 ++++++++++--------- .../ota/IotOtaUpgradeRecordServiceImpl.java | 4 +++- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index 83bdb43065..bba148a594 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -82,18 +81,20 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX + * 该函数用于将符合指定任务ID和状态的升级记录的状态更新为新的状态。 * - * @param taskId 任务ID,用于查找对应的升级记录。 + * @param setStatus 要设置的新状态值,类型为Integer + * @param taskId 要更新的升级记录对应的任务ID,类型为Long + * @param whereStatus 用于筛选升级记录的当前状态值,类型为Integer */ - default void cancelUpgradeRecordByTaskId(Long taskId) { - // 使用LambdaUpdateWrapper构建更新条件,将状态为“待处理”的记录更新为“已取消” - // TODO @li:哪些可以更新,通过 service 传递。mapper 尽量不要有逻辑 + default void updateUpgradeRecordStatusByTaskIdAndStatus(Integer setStatus, Long taskId, Integer whereStatus) { + // 使用LambdaUpdateWrapper构建更新条件,将指定状态的记录更新为指定状态 update(new LambdaUpdateWrapper() - .set(IotOtaUpgradeRecordDO::getStatus, IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()) + .set(IotOtaUpgradeRecordDO::getStatus, setStatus) .eq(IotOtaUpgradeRecordDO::getTaskId, taskId) - .eq(IotOtaUpgradeRecordDO::getStatus, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()) + .eq(IotOtaUpgradeRecordDO::getStatus, whereStatus) ); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java index ae2b682739..5738f57637 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java @@ -134,7 +134,9 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Override public void cancelUpgradeRecordByTaskId(Long taskId) { // 暂定只有待推送的升级记录可以取消 - upgradeRecordMapper.cancelUpgradeRecordByTaskId(taskId); + upgradeRecordMapper.updateUpgradeRecordStatusByTaskIdAndStatus( + IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus(), taskId, + IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); } @Override From 4e33cd2bde4917307a0429f14986a89b3a57e160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=8E=84=E7=A4=BC?= Date: Thu, 27 Feb 2025 23:28:04 +0800 Subject: [PATCH 218/309] =?UTF-8?q?refactor(iot):=20=E4=BC=98=E5=8C=96=20O?= =?UTF-8?q?TA=20=E7=9B=B8=E5=85=B3=20Mapper=20=E6=8E=A5=E5=8F=A3=E7=9A=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除了多余的 TODO 注释 - 更新了 IotOtaFirmwareMapper、IotOtaUpgradeRecordMapper 和 IotOtaUpgradeTaskMapper 的类注释 - 统一了注释格式,增加了作者信息 --- .../module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java | 6 +++--- .../iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java | 6 +++--- .../module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java | 7 +++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java index 9bd4619164..9652942b22 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java @@ -9,10 +9,10 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; -// TODO @li:这里的注释,可以去掉哈,多了点点 /** - * IotOtaFirmwareMapper 接口用于操作 IotOtaFirmwareDO 实体类对应的数据库表。 - * 该接口继承自 BaseMapperX,提供了基本的 CRUD 操作,并扩展了特定查询方法。 + * OTA固件 Mapper + * + * @author Shelly */ @Mapper public interface IotOtaFirmwareMapper extends BaseMapperX { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index bba148a594..b48131c61a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -11,10 +11,10 @@ import org.apache.ibatis.annotations.Param; import java.util.List; -// TODO @li:这里的注释,可以去掉哈,多了点点 - /** - * OTA 升级记录 Mapper 接口 + * OTA 升级记录 Mapper + * + * @author Shelly */ @Mapper public interface IotOtaUpgradeRecordMapper extends BaseMapperX { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java index 80c3ff5654..d955b13619 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeTaskMapper.java @@ -9,10 +9,10 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; -// TODO @li:这里的注释,可以去掉哈,多了点点 /** - * IotOtaUpgradeTaskMapper 接口用于操作 IotOtaUpgradeTaskDO 数据库表。 - * 该接口继承自 BaseMapperX,提供了基本的数据库操作方法。 + * OTA 升级任务Mapper + * + * @author Shelly */ @Mapper public interface IotOtaUpgradeTaskMapper extends BaseMapperX { @@ -54,5 +54,4 @@ public interface IotOtaUpgradeTaskMapper extends BaseMapperX Date: Fri, 28 Feb 2025 07:43:43 +0800 Subject: [PATCH 219/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E7=9F=A5=E8=AF=86=E5=BA=93?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=9A=84=E8=A1=A8=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 11 +- .../AiKnowledgeDocumentStatusEnum.java | 39 ----- .../knowledge/AiKnowledgeController.http | 33 ++++ .../knowledge/AiKnowledgeController.java | 16 +- .../AiKnowledgeDocumentController.http | 12 ++ .../AiKnowledgeDocumentController.java | 17 +- .../AiKnowledgeDocumentUpdateReqVO.java | 7 +- .../AiKnowledgeDocumentCreateReqVO.java | 22 +-- ...teReqVO.java => AiKnowledgeSaveReqVO.java} | 28 ++-- .../vo/knowledge/AiKnowledgeUpdateReqVO.java | 32 ---- .../dataobject/chat/AiChatConversationDO.java | 2 + .../dal/dataobject/chat/AiChatMessageDO.java | 2 + .../ai/dal/dataobject/image/AiImageDO.java | 1 + .../dataobject/knowledge/AiKnowledgeDO.java | 28 +--- .../knowledge/AiKnowledgeDocumentDO.java | 44 +---- .../knowledge/AiKnowledgeSegmentDO.java | 19 ++- .../dal/dataobject/mindmap/AiMindMapDO.java | 1 + .../dal/dataobject/model/AiChatModelDO.java | 1 + .../ai/dal/dataobject/music/AiMusicDO.java | 1 + .../ai/dal/dataobject/write/AiWriteDO.java | 1 + .../mysql/knowledge/AiKnowledgeMapper.java | 6 +- .../knowledge/AiKnowledgeDocumentService.java | 10 +- .../AiKnowledgeDocumentServiceImpl.java | 79 ++++----- .../knowledge/AiKnowledgeSegmentService.java | 8 + .../AiKnowledgeSegmentServiceImpl.java | 155 +++++++++++++----- .../service/knowledge/AiKnowledgeService.java | 21 +-- .../knowledge/AiKnowledgeServiceImpl.java | 45 ++--- .../ai/service/model/AiApiKeyService.java | 13 +- .../ai/service/model/AiApiKeyServiceImpl.java | 25 ++- .../ai/core/factory/AiModelFactory.java | 10 +- .../ai/core/factory/AiModelFactoryImpl.java | 32 +++- 31 files changed, 356 insertions(+), 365 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/{AiKnowledgeCreateReqVO.java => AiKnowledgeSaveReqVO.java} (69%) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index e1dd1a9568..48913e91fa 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -21,7 +21,7 @@ public interface ErrorCodeConstants { ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!"); ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型"); - // ========== API 聊天模型 1-040-002-000 ========== + // ========== API 聊天角色 1-040-002-000 ========== ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!"); @@ -40,7 +40,6 @@ public interface ErrorCodeConstants { ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!"); ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}"); ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); - ErrorCode IMAGE_FAIL = new ErrorCode(1_022_005_002, "图片绘画失败! {}"); // ========== API 音乐 1-040-006-000 ========== ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!"); @@ -54,7 +53,11 @@ public interface ErrorCodeConstants { // ========== API 知识库 1-022-008-000 ========== ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!"); - ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!"); - ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!"); + + ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_101, "文档不存在!"); + ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_022_008_102, "文档内容为空!"); + ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_022_008_102, "文档加载失败!"); + + ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_202, "段落不存在!"); } diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java deleted file mode 100644 index 6ded3f6de8..0000000000 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/knowledge/AiKnowledgeDocumentStatusEnum.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.ai.enums.knowledge; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * AI 知识库-文档状态的枚举 - * - * @author xiaoxin - */ -@AllArgsConstructor -@Getter -public enum AiKnowledgeDocumentStatusEnum implements ArrayValuable { - - IN_PROGRESS(10, "索引中"), - SUCCESS(20, "可用"), - FAIL(30, "失败"); - - /** - * 状态 - */ - private final Integer status; - - /** - * 状态名 - */ - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiKnowledgeDocumentStatusEnum::getStatus).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http new file mode 100644 index 0000000000..235e880343 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http @@ -0,0 +1,33 @@ +### 创建知识库 +POST {{baseUrl}}/ai/knowledge/create +Content-Type: application/json +Authorization: {{token}} +tenant-id: {{adminTenantId}} + +{ + "name": "测试标题", + "description": "测试描述", + "embeddingModelId": 30, + "topK": 3, + "similarityThreshold": 0.5 +} + +### 更新知识库 +PUT {{baseUrl}}/ai/knowledge/update +Content-Type: application/json +Authorization: {{token}} +tenant-id: {{adminTenantId}} + +{ + "id": 1, + "name": "测试标题(更新)", + "description": "测试描述", + "embeddingModelId": 30, + "topK": 5, + "similarityThreshold": 0.6 +} + +### 获取知识库分页 +GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10 +Authorization: {{token}} +tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index 3ffea5e802..5c27810260 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -3,10 +3,9 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; import io.swagger.v3.oas.annotations.Operation; @@ -17,7 +16,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - AI 知识库") @RestController @@ -31,20 +29,22 @@ public class AiKnowledgeController { @GetMapping("/page") @Operation(summary = "获取知识库分页") public CommonResult> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { - PageResult pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO); + + PageResult pageResult = knowledgeService.getKnowledgePage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); } @PostMapping("/create") @Operation(summary = "创建知识库") - public CommonResult createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) { - return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId())); + public CommonResult createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) { + return success(knowledgeService.createKnowledge(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新知识库") - public CommonResult updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) { - knowledgeService.updateKnowledge(updateReqVO, getLoginUserId()); + public CommonResult updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) { + knowledgeService.updateKnowledge(updateReqVO); return success(true); } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http new file mode 100644 index 0000000000..7af4780c3c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http @@ -0,0 +1,12 @@ +### 创建知识文档 +POST {{baseUrl}}/ai/knowledge/document/create +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +{ + "knowledgeId": 1, + "name": "测试文档", + "url": "https://static.iocoder.cn/README.md", + "segmentMaxTokens": 800 +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index 75c4d805b6..cd6feb3056 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -27,20 +27,21 @@ public class AiKnowledgeDocumentController { @Resource private AiKnowledgeDocumentService documentService; - @PostMapping("/create") - @Operation(summary = "新建文档") - public CommonResult createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) { - Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); - return success(knowledgeDocumentId); - } - @GetMapping("/page") @Operation(summary = "获取文档分页") - public CommonResult> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) { + public CommonResult> getKnowledgeDocumentPage( + @Valid AiKnowledgeDocumentPageReqVO pageReqVO) { PageResult pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); } + @PostMapping("/create") + @Operation(summary = "新建文档") + public CommonResult createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) { + Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); + return success(knowledgeDocumentId); + } + @PutMapping("/update") @Operation(summary = "更新文档") public CommonResult updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java index 2cc6a32f3c..13f871f6d4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java @@ -11,16 +11,15 @@ import lombok.Data; @Data public class AiKnowledgeDocumentUpdateReqVO { - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") @NotNull(message = "编号不能为空") private Long id; + @Schema(description = "名称", example = "Java 开发手册") + private String name; + @Schema(description = "是否启用", example = "1") @InEnum(CommonStatusEnum.class) private Integer status; - @Schema(description = "名称", example = "Java 开发手册") - private String name; - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java index df6b6821d8..1d2e49307a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java @@ -23,24 +23,8 @@ public class AiKnowledgeDocumentCreateReqVO { @URL(message = "文档 URL 格式不正确") private String url; - @Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") - @NotNull(message = "每个段落的目标 token 数不能为空") - private Integer defaultSegmentTokens; - - @Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350") - @NotNull(message = "每个段落的最小字符数不能为空") - private Integer minSegmentWordCount; - - @Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - @NotNull(message = "丢弃阈值不能为空") - private Integer minChunkLengthToEmbed; - - @Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") - @NotNull(message = "最大段落数不能为空") - private Integer maxNumSegments; - - @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "分块是否保留分隔符不能为空") - private Boolean keepSeparator; + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") + @NotNull(message = "分段的最大 Token 数不能为空") + private Integer segmentMaxTokens; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java similarity index 69% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java index 00843665c4..f7c6a245ca 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java @@ -5,11 +5,12 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; -import java.util.List; - -@Schema(description = "管理后台 - AI 知识库创建 Request VO") +@Schema(description = "管理后台 - AI 知识库新增/修改 Request VO") @Data -public class AiKnowledgeCreateReqVO { +public class AiKnowledgeSaveReqVO { + + @Schema(description = "对话编号", example = "1204") + private Long id; @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南") @NotBlank(message = "知识库名称不能为空") @@ -18,19 +19,18 @@ public class AiKnowledgeCreateReqVO { @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档") private String description; - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]") - private List visibilityPermissions; - - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "嵌入模型不能为空") - private Long modelId; - - @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5") - @NotNull(message = "相似性阈值不能为空") - private Double similarityThreshold; + @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "向量模型不能为空") + private Long embeddingModelId; @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") @NotNull(message = "topK 不能为空") private Integer topK; + @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5") + @NotNull(message = "相似性阈值不能为空") + private Double similarityThreshold; + + // TODO @芋艿:status + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java deleted file mode 100644 index ba98bf0c72..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.List; - -@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO") -@Data -public class AiKnowledgeUpdateReqVO { - - @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") - @NotNull(message = "知识库编号不能为空") - private Long id; - - @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "") - @NotBlank(message = "知识库名称不能为空") - private String name; - - @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "") - private String description; - - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - private List visibilityPermissions; - - @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "嵌入模型不能为空") - private Long modelId; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java index 7d9625f58f..cc8ae7d42a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java @@ -80,6 +80,8 @@ public class AiChatConversationDO extends BaseDO { private Long modelId; /** * 模型标志 + * + * 冗余 {@link AiChatModelDO#getModel()} 字段 */ private String model; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java index ecd10609f5..c65360ccbb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java @@ -82,6 +82,8 @@ public class AiChatMessageDO extends BaseDO { /** * 模型标志 + * + * 冗余 {@link AiChatModelDO#getModel()} */ private String model; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java index 56749a1d00..5b43bc91e9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java @@ -52,6 +52,7 @@ public class AiImageDO extends BaseDO { * 枚举 {@link cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum} */ private String platform; + // TODO @芋艿:modelId? /** * 模型 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java index 638a8ba50b..732d54e24a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java @@ -2,15 +2,12 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; 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 lombok.Data; -import java.util.List; - /** * AI 知识库 DO * @@ -26,12 +23,6 @@ public class AiKnowledgeDO extends BaseDO { */ @TableId private Long id; - /** - * 用户编号 - *

- * 关联 AdminUserDO 的 userId 字段 - */ - private Long userId; /** * 知识库名称 */ @@ -42,20 +33,17 @@ public class AiKnowledgeDO extends BaseDO { private String description; /** - * 可见权限,选择哪些人可见 - *

- * -1 所有人可见,其他为各自用户编号 + * 向量模型编号 + * + * 关联 {@link AiChatModelDO#getId()} */ - @TableField(typeHandler = LongListTypeHandler.class) - private List visibilityPermissions; - /** - * 嵌入模型编号 - */ - private Long modelId; + private Long embeddingModelId; /** * 模型标识 + * + * 冗余 {@link AiChatModelDO#getModel()} */ - private String model; + private String embeddingModel; /** * topK diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java index ee8bfd5aab..56de374f7e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -33,54 +32,27 @@ public class AiKnowledgeDocumentDO extends BaseDO { * 文件名称 */ private String name; + /** + * 文件 URL + */ + private String url; /** * 内容 */ private String content; /** - * 文件 URL + * 文档长度 */ - private String url; + private Integer contentLength; + /** * 文档 token 数量 */ private Integer tokens; - /** - * 文档字符数 - */ - private Integer wordCount; - - - // ========== 自定义分段所用参数 ========== - // TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。 /** * 每个文本块的目标 token 数 */ - private Integer defaultSegmentTokens; - /** - * 每个文本块的最小字符数 - */ - private Integer minSegmentWordCount; - /** - * 低于此值的块会被丢弃 - */ - private Integer minChunkLengthToEmbed; - /** - * 最大块数 - */ - private Integer maxNumSegments; - /** - * 分块是否保留分隔符 - */ - private Boolean keepSeparator; - // =================================== - - /** - * 切片状态 - *

- * 枚举 {@link AiKnowledgeDocumentStatusEnum} - */ - private Integer sliceStatus; + private Integer segmentMaxTokens; /** * 状态 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index b08e960d14..5ab18e7d7a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -17,17 +17,16 @@ import lombok.Data; @Data public class AiKnowledgeSegmentDO extends BaseDO { - public static final String FIELD_KNOWLEDGE_ID = "knowledgeId"; + /** + * 向量库的编号 - 空值 + */ + public static final String VECTOR_ID_EMPTY = ""; /** * 编号 */ @TableId private Long id; - /** - * 向量库的编号 - */ - private String vectorId; /** * 知识库编号 *

@@ -45,13 +44,19 @@ public class AiKnowledgeSegmentDO extends BaseDO { */ private String content; /** - * 字符数 + * 切片内容长度 */ - private Integer wordCount; + private Integer contentLength; + + /** + * 向量库的编号 + */ + private String vectorId; /** * token 数量 */ private Integer tokens; + /** * 状态 *

diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java index b9768529f1..47aa0ce4a5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java @@ -36,6 +36,7 @@ public class AiMindMapDO extends BaseDO { * 枚举 {@link AiPlatformEnum} */ private String platform; + // TODO @芋艿:modelId? /** * 模型 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java index 7197f8b58f..c3800bd4ca 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java @@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; +// TODO @芋艿,需要改造,增加 type /** * AI 聊天模型 DO * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java index e03d62c162..bfa7394ddd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java @@ -84,6 +84,7 @@ public class AiMusicDO extends BaseDO { * 枚举 {@link AiPlatformEnum} */ private String platform; + // TODO @芋艿:modelId? /** * 模型 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java index 0d6f9c5e64..a99f94e82d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java @@ -44,6 +44,7 @@ public class AiWriteDO extends BaseDO { * 枚举 {@link AiPlatformEnum} */ private String platform; + // TODO @芋艿:modelId? /** * 模型 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java index f07a9a2afa..735332beb9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java @@ -16,11 +16,9 @@ import org.apache.ibatis.annotations.Mapper; @Mapper public interface AiKnowledgeMapper extends BaseMapperX { - default PageResult selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) { + default PageResult selectPage(AiKnowledgePageReqVO pageReqVO) { return selectPage(pageReqVO, new LambdaQueryWrapperX() .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) - .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName()) - .and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)"))) - .orderByDesc(AiKnowledgeDO::getId)); + .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 3de0ac01de..9cb7c5a827 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -21,7 +21,6 @@ public interface AiKnowledgeDocumentService { */ Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); - /** * 获取文档分页 * @@ -36,4 +35,13 @@ public interface AiKnowledgeDocumentService { * @param reqVO 更新信息 */ void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO); + + /** + * 校验文档是否存在 + * + * @param id 文档编号 + * @return 文档信息 + */ + AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index f5855db7d9..0f48d5a992 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -1,26 +1,22 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; -import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.tokenizer.TokenCountEstimator; -import org.springframework.ai.transformer.splitter.TokenTextSplitter; -import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.context.annotation.Lazy; import org.springframework.core.io.ByteArrayResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -28,7 +24,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** * AI 知识库文档 Service 实现类 @@ -40,58 +36,46 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCU public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentService { @Resource - private AiKnowledgeDocumentMapper documentMapper; - @Resource - private AiKnowledgeSegmentMapper segmentMapper; + private AiKnowledgeDocumentMapper knowledgeDocumentMapper; @Resource private TokenCountEstimator tokenCountEstimator; + @Resource + private AiKnowledgeSegmentService knowledgeSegmentService; + @Resource + @Lazy // 延迟加载,避免循环依赖 private AiKnowledgeService knowledgeService; @Override @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { - // 0. 校验并获取向量存储实例 - VectorStore vectorStore = knowledgeService.getVectorStoreById(createReqVO.getKnowledgeId()); + // 1. 校验参数 + knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); - // 1.1 下载文档 + // 2. 下载文档 TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl())); List documents = loader.get(); Document document = CollUtil.getFirst(documents); - // 1.2 文档记录入库 - String content = document.getText(); - AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) - .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length()) - .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus()); - documentMapper.insert(documentDO); - Long documentId = documentDO.getId(); - if (CollUtil.isEmpty(documents)) { - return documentId; + if (document == null || StrUtil.isEmpty(document.getText())) { + throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL); } - // 2 构造文本分段器 - TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(), - createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator()); - // 2.1 文档分段 - List segments = tokenTextSplitter.apply(documents); - // 2.2 分段内容入库 - List segmentDOList = CollectionUtils.convertList(segments, - segment -> new AiKnowledgeSegmentDO().setContent(segment.getText()).setDocumentId(documentId) - .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId()) - .setTokens(tokenCountEstimator.estimate(segment.getText())).setWordCount(segment.getText().length()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - segmentMapper.insertBatch(segmentDOList); + // 3. 文档记录入库 + String content = document.getText(); + AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) + .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content)) + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + knowledgeDocumentMapper.insert(documentDO); - // 3. 向量化并存储 - segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId())); - vectorStore.add(segments); - return documentId; + // 4. 文档切片入库 + knowledgeSegmentService.createKnowledgeSegmentBySplitContent(documentDO.getId(), document.getText()); + return documentDO.getId(); } @Override public PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) { - return documentMapper.selectPage(pageReqVO); + return knowledgeDocumentMapper.selectPage(pageReqVO); } @Override @@ -100,17 +84,13 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic validateKnowledgeDocumentExists(reqVO.getId()); // 2. 更新文档 AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); - documentMapper.updateById(document); + knowledgeDocumentMapper.updateById(document); + // TODO @芋艿:这里要处理状态的变更 } - /** - * 校验文档是否存在 - * - * @param id 文档编号 - * @return 文档信息 - */ - private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { - AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id); + @Override + public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { + AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id); if (knowledgeDocument == null) { throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS); } @@ -120,6 +100,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic private org.springframework.core.io.Resource downloadFile(String url) { try { byte[] bytes = HttpUtil.downloadBytes(url); + if (bytes.length == 0) { + throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY); + } return new ByteArrayResource(bytes); } catch (Exception e) { log.error("[downloadFile][url({}) 下载失败]", url, e); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 91bffc2761..2a8d9afd87 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -24,6 +24,14 @@ public interface AiKnowledgeSegmentService { */ PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO); + /** + * 基于 content 内容,切片创建多个段落 + * + * @param documentId 知识库文档编号 + * @param content 文档内容 + */ + void createKnowledgeSegmentBySplitContent(Long documentId, String content); + /** * 更新段落的内容 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index afb9d755f0..9b15135c95 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -10,23 +11,28 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; +import org.springframework.ai.tokenizer.TokenCountEstimator; +import org.springframework.ai.transformer.splitter.TextSplitter; +import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; +import java.util.Collections; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; /** @@ -38,85 +44,138 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM @Slf4j public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { + public static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId"; + public static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId"; + public static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId"; + @Resource private AiKnowledgeSegmentMapper segmentMapper; @Resource private AiKnowledgeService knowledgeService; @Resource - private AiChatModelService chatModelService; + @Lazy // 延迟加载,避免循环依赖 + private AiKnowledgeDocumentService knowledgeDocumentService; @Resource private AiApiKeyService apiKeyService; + @Resource + private TokenCountEstimator tokenCountEstimator; + @Override public PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) { return segmentMapper.selectPage(pageReqVO); } + @Override + public void createKnowledgeSegmentBySplitContent(Long documentId, String content) { + // 1. 校验 + AiKnowledgeDocumentDO documentDO = knowledgeDocumentService.validateKnowledgeDocumentExists(documentId); + AiKnowledgeDO knowledgeDO = knowledgeService.validateKnowledgeExists(documentDO.getKnowledgeId()); + VectorStore vectorStore = getVectorStoreById(knowledgeDO); + + // 2. 文档切片 + Document document = new Document(content); + TextSplitter textSplitter = buildTokenTextSplitter(documentDO.getSegmentMaxTokens()); + List documentSegments = textSplitter.apply(Collections.singletonList(document)); + + // 3.1 存储切片 + List segmentDOs = convertList(documentSegments, segment -> { + if (StrUtil.isEmpty(segment.getText())) { + return null; + } + return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId) + .setContent(segment.getText()).setContentLength(segment.getText().length()) + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY).setTokens(tokenCountEstimator.estimate(segment.getText())) + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + segmentMapper.insertBatch(segmentDOs); + // 3.2 切片向量化 + for (int i = 0; i < documentSegments.size(); i++) { + Document segment = documentSegments.get(i); + AiKnowledgeSegmentDO segmentDO = segmentDOs.get(i); + writeVectorStore(vectorStore, segmentDO, segment); + } + } + @Override public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) { // 1. 校验 - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); + AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId()); - // 2.1 获取知识库向量实例 - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); - // 2.2 删除原向量 - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); - // 2.3 重新向量化 - Document document = new Document(reqVO.getContent()); - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); - vectorStore.add(List.of(document)); + // 2. 删除向量 + VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId()); + deleteVectorStore(vectorStore, segment); - // 3. 更新段落内容 - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); - knowledgeSegment.setVectorId(document.getId()); - segmentMapper.updateById(knowledgeSegment); + // 3.1 更新切片 + AiKnowledgeSegmentDO segmentDO = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); + segmentMapper.updateById(segmentDO); + // 3.2 重新向量化 + writeVectorStore(vectorStore, segmentDO, new Document(segmentDO.getContent())); } @Override public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) { - // 0 校验 - AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); - // 1 获取知识库向量实例 - VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); - AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); + // 1. 校验 + AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId()); + // 2. 获取知识库向量实例 + VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId()); + + // 3. 更新状态 + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus())); + + // 4. 更新向量 if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - // 2.1 启用重新向量化 - Document document = new Document(oldKnowledgeSegment.getContent()); - document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); - vectorStore.add(List.of(document)); - knowledgeSegment.setVectorId(document.getId()); + writeVectorStore(vectorStore, segment, new Document(segment.getContent())); } else { - // 2.2 禁用删除向量 - vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); - knowledgeSegment.setVectorId(""); + deleteVectorStore(vectorStore, segment); } - // 3 更新段落状态 - segmentMapper.updateById(knowledgeSegment); + } + + private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) { + // 1. 向量存储 + segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId()); + segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId()); + segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId()); + vectorStore.add(List.of(segment)); + + // 2. 更新向量 ID + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()).setVectorId(segment.getId())); + } + + private void deleteVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO) { + // 1. 更新向量 ID + if (StrUtil.isEmpty(segmentDO.getVectorId())) { + return; + } + segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(segmentDO.getId()) + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)); + + // 2. 删除向量 + vectorStore.delete(List.of(segmentDO.getVectorId())); } @Override public List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) { // 1. 校验 AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId()); - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); // 2. 获取向量存储实例 - VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); + VectorStore vectorStore = apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId()); // 3.1 向量检索 - List documentList = vectorStore.similaritySearch(SearchRequest.builder() + List documents = vectorStore.similaritySearch(SearchRequest.builder() .query(reqVO.getContent()) - .topK(knowledge.getTopK()) - .similarityThreshold(knowledge.getSimilarityThreshold()) - .filterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()) + .topK(knowledge.getTopK()).similarityThreshold(knowledge.getSimilarityThreshold()) + .filterExpression(new FilterExpressionBuilder() + .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()) .build()); - if (CollUtil.isEmpty(documentList)) { + if (CollUtil.isEmpty(documents)) { return ListUtil.empty(); } // 3.2 段落召回 - return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class)); + return segmentMapper.selectListByVectorIds(convertList(documents, Document::getId)); } /** @@ -133,4 +192,22 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService return knowledgeSegment; } + private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) { + return apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId()); + } + + private VectorStore getVectorStoreById(Long knowledgeId) { + return getVectorStoreById(knowledgeService.validateKnowledgeExists(knowledgeId)); + } + + private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) { + return TokenTextSplitter.builder() + .withChunkSize(segmentMaxTokens) + .withMinChunkSizeChars(Integer.MAX_VALUE) // 忽略字符的截断 + .withMinChunkLengthToEmbed(1) // 允许的最小有效分段长度 + .withMaxNumChunks(Integer.MAX_VALUE) + .withKeepSeparator(true) // 保留分隔符 + .build(); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java index 7060076a42..5c454383f9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java @@ -1,11 +1,9 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import org.springframework.ai.vectorstore.VectorStore; /** * AI 知识库-基础信息 Service 接口 @@ -18,18 +16,16 @@ public interface AiKnowledgeService { * 创建知识库 * * @param createReqVO 创建信息 - * @param userId 用户编号 * @return 编号 */ - Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId); + Long createKnowledge(AiKnowledgeSaveReqVO createReqVO); /** * 更新知识库 * * @param updateReqVO 更新信息 - * @param userId 用户编号 */ - void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId); + void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO); /** * 校验知识库是否存在 @@ -41,18 +37,9 @@ public interface AiKnowledgeService { /** * 获得知识库分页 * - * @param userId 用户编号 * @param pageReqVO 分页查询 * @return 知识库分页 */ - PageResult getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO); - - /** - * 根据知识库编号获取向量存储实例 - * - * @param id 知识库编号 - * @return 向量存储实例 - */ - VectorStore getVectorStoreById(Long id); + PageResult getKnowledgePage(AiKnowledgePageReqVO pageReqVO); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 1a000c19d1..583eabaeaa 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -1,12 +1,10 @@ package cn.iocoder.yudao.module.ai.service.knowledge; -import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; @@ -34,35 +32,30 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @Resource private AiChatModelService chatModelService; - @Resource - private AiApiKeyService apiKeyService; @Override - public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) { + public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) { // 1. 校验模型配置 - AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId()); + AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getEmbeddingModelId()); // 2. 插入知识库 - AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) - .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus()); - knowledgeMapper.insert(knowledgeBase); - return knowledgeBase.getId(); + AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) + .setEmbeddingModel(model.getModel()).setStatus(CommonStatusEnum.ENABLE.getStatus()); + knowledgeMapper.insert(knowledge); + return knowledge.getId(); } @Override - public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) { + public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) { // 1.1 校验知识库存在 - AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId()); - if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) { - throw exception(KNOWLEDGE_NOT_EXISTS); - } + validateKnowledgeExists(updateReqVO.getId()); // 1.2 校验模型配置 - AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId()); + AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getEmbeddingModelId()); // 2. 更新知识库 - AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class); - updateDO.setModel(model.getModel()); - knowledgeMapper.updateById(updateDO); + AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class) + .setEmbeddingModel(model.getModel()); + knowledgeMapper.updateById(updateObj); } @Override @@ -75,16 +68,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { } @Override - public PageResult getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) { - return knowledgeMapper.selectPage(userId, pageReqVO); - } - - @Override - public VectorStore getVectorStoreById(Long id) { - AiKnowledgeDO knowledge = validateKnowledgeExists(id); - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); - // 创建或获取 VectorStore 对象 - return apiKeyService.getOrCreateVectorStore(model.getKeyId()); + public PageResult getKnowledgePage(AiKnowledgePageReqVO pageReqVO) { + return knowledgeMapper.selectPage(pageReqVO); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java index f5f8813492..29d9e3e07a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java @@ -9,7 +9,6 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveR import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; import jakarta.validation.Valid; import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.vectorstore.VectorStore; @@ -113,20 +112,12 @@ public interface AiApiKeyService { */ SunoApi getSunoApi(); - /** - * 获得 EmbeddingModel 对象 - * - * @param id 编号 - * @return EmbeddingModel 对象 - */ - EmbeddingModel getEmbeddingModel(Long id); - /** * 获得 VectorStore 对象 * - * @param id 编号 + * @param modelId 编号 * @return VectorStore 对象 */ - VectorStore getOrCreateVectorStore(Long id); + VectorStore getOrCreateVectorStoreByModelId(Long modelId); } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index 50e1fbd7ac..1ef7640f29 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -10,12 +10,14 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; import jakarta.annotation.Resource; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -36,6 +38,11 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @Resource private AiApiKeyMapper apiKeyMapper; + // TODO @芋艿:后续要不要改? + @Resource + @Lazy // 延迟加载,解决渲染依赖 + private AiChatModelService chatModelService; + @Resource private AiModelFactory modelFactory; @@ -136,18 +143,18 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { } @Override - public EmbeddingModel getEmbeddingModel(Long id) { - AiApiKeyDO apiKey = validateApiKey(id); + public VectorStore getOrCreateVectorStoreByModelId(Long modelId) { + // 获取模型 + 密钥 + AiChatModelDO chatModel = chatModelService.validateChatModel(modelId); + AiApiKeyDO apiKey = validateApiKey(chatModel.getKeyId()); AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); - return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl()); - } - @Override - public VectorStore getOrCreateVectorStore(Long id) { - AiApiKeyDO apiKey = validateApiKey(id); - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + // 创建或获取 EmbeddingModel 对象 + EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), + apiKey.getUrl(), chatModel.getModel()); + // 创建或获取 VectorStore 对象 - return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); + return modelFactory.getOrCreateVectorStore(embeddingModel); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java index 243c4ae4bc..d4275d5e5d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java @@ -89,21 +89,19 @@ public interface AiModelFactory { * @param platform 平台 * @param apiKey API KEY * @param url API URL + * @param model 模型 * @return ChatModel 对象 */ - EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url); + EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model); /** * 基于指定配置,获得 VectorStore 对象 *

* 如果不存在,则进行创建 * - * @param embeddingModel 嵌入模型 - * @param platform 平台 - * @param apiKey API KEY - * @param url API URL + * @param embeddingModel 向量模型 * @return VectorStore 对象 */ - VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url); + VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index e07347334b..779bd8e2fb 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -21,6 +21,7 @@ import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; +import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; import com.azure.ai.openai.OpenAIClientBuilder; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; @@ -33,10 +34,13 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.document.MetadataMode; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.ollama.OllamaChatModel; +import org.springframework.ai.ollama.OllamaEmbeddingModel; import org.springframework.ai.ollama.api.OllamaApi; +import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.api.OpenAiApi; @@ -184,13 +188,15 @@ public class AiModelFactoryImpl implements AiModelFactory { } @Override - public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) { - String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url); + public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) { + String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model); return Singleton.get(cacheKey, (Func0) () -> { - // TODO @xin 先测试一个 switch (platform) { case TONG_YI: - return buildTongYiEmbeddingModel(apiKey); + return buildTongYiEmbeddingModel(apiKey, model); + case OLLAMA: + return buildOllamaEmbeddingModel(url, model); + // TODO @芋艿:各个平台的向量化能力; default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -198,13 +204,14 @@ public class AiModelFactoryImpl implements AiModelFactory { } @Override - public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) { - String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url); + public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel) { +// String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url); + String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel); return Singleton.get(cacheKey, (Func0) () -> { - String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey); // TODO @芋艿:先临时使用 store return SimpleVectorStore.builder(embeddingModel).build(); // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的 +// String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey); // var config = RedisVectorStore.RedisVectorStoreConfig.builder() // .withIndexName(cacheKey) // .withPrefix(prefix) @@ -388,9 +395,16 @@ public class AiModelFactoryImpl implements AiModelFactory { /** * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeEmbeddingModel 方法 */ - private EmbeddingModel buildTongYiEmbeddingModel(String apiKey) { + private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { DashScopeApi dashScopeApi = new DashScopeApi(apiKey); - return new DashScopeEmbeddingModel(dashScopeApi); + DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); + return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); + } + + private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { + OllamaApi ollamaApi = new OllamaApi(url); + OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build(); + return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).defaultOptions(ollamaOptions).build(); } } From 7918ba7d29698a39dc0141e20284d4946d13fa9a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 28 Feb 2025 08:16:44 +0800 Subject: [PATCH 220/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E6=96=87=E6=A1=A3=E7=9A=84=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 1 + .../AiKnowledgeDocumentController.http | 25 +++++++- .../AiKnowledgeDocumentController.java | 15 ++++- .../AiKnowledgeDocumentCreateListReqVO.java | 42 +++++++++++++ .../knowledge/AiKnowledgeDocumentService.java | 11 ++++ .../AiKnowledgeDocumentServiceImpl.java | 60 +++++++++++++++---- .../knowledge/AiKnowledgeSegmentService.java | 12 ++++ .../AiKnowledgeSegmentServiceImpl.java | 6 +- 8 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 48913e91fa..5ac6d2ae9e 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -56,6 +56,7 @@ public interface ErrorCodeConstants { ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_101, "文档不存在!"); ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_022_008_102, "文档内容为空!"); + ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_022_008_102, "文件下载失败!"); ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_022_008_102, "文档加载失败!"); ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_202, "段落不存在!"); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http index 7af4780c3c..22c1d91115 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http @@ -9,4 +9,27 @@ tenant-id: {{adminTenantId}} "name": "测试文档", "url": "https://static.iocoder.cn/README.md", "segmentMaxTokens": 800 -} \ No newline at end of file +} + +### 批量创建知识文档 +POST {{baseUrl}}/ai/knowledge/document/create-list +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +{ + "knowledgeId": 1, + "list": [ + { + "name": "测试文档1", + "url": "https://static.iocoder.cn/README.md", + "segmentMaxTokens": 800 + }, + { + "name": "测试文档2", + "url": "https://static.iocoder.cn/README_yudao.md", + "segmentMaxTokens": 400 + } + ] +} + diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index cd6feb3056..8d61d0c1e4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; @@ -16,6 +17,8 @@ import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管理后台 - AI 知识库文档") @@ -38,8 +41,16 @@ public class AiKnowledgeDocumentController { @PostMapping("/create") @Operation(summary = "新建文档") public CommonResult createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) { - Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO); - return success(knowledgeDocumentId); + Long id = documentService.createKnowledgeDocument(reqVO); + return success(id); + } + + @PostMapping("/create-list") + @Operation(summary = "批量新建文档") + public CommonResult> createKnowledgeDocumentList( + @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) { + List ids = documentService.createKnowledgeDocumentList(reqVO); + return success(ids); } @PutMapping("/update") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java new file mode 100644 index 0000000000..4bd817aa4c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import java.util.List; + +@Schema(description = "管理后台 - AI 知识库文档批量创建 Request VO") +@Data +public class AiKnowledgeDocumentCreateListReqVO { + + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204") + @NotNull(message = "知识库编号不能为空") + private Long knowledgeId; + + @Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "文档列表不能为空") + private List list; + + @Schema(description = "文档") + @Data + public static class Document { + + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆") + @NotBlank(message = "文档名称不能为空") + private String name; + + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") + @URL(message = "文档 URL 格式不正确") + private String url; + + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") + @NotNull(message = "分段的最大 Token 数不能为空") + private Integer segmentMaxTokens; + + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 9cb7c5a827..41fa41527d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -4,8 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import java.util.List; + /** * AI 知识库-文档 Service 接口 * @@ -21,6 +24,14 @@ public interface AiKnowledgeDocumentService { */ Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); + /** + * 批量创建文档 + * + * @param createListReqVO 批量创建 Request VO + * @return 文档编号列表 + */ + List createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO); + /** * 获取文档分页 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 0f48d5a992..fd45b97062 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; @@ -21,9 +22,11 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** @@ -54,25 +57,45 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); // 2. 下载文档 - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl())); - List documents = loader.get(); - Document document = CollUtil.getFirst(documents); - if (document == null || StrUtil.isEmpty(document.getText())) { - throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL); - } + String content = readUrl(createReqVO.getUrl()); // 3. 文档记录入库 - String content = document.getText(); AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class) .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content)) .setStatus(CommonStatusEnum.ENABLE.getStatus()); knowledgeDocumentMapper.insert(documentDO); - // 4. 文档切片入库 - knowledgeSegmentService.createKnowledgeSegmentBySplitContent(documentDO.getId(), document.getText()); + // 4. 文档切片入库(同步) + knowledgeSegmentService.createKnowledgeSegmentBySplitContent(documentDO.getId(), content); return documentDO.getId(); } + @Override + @Transactional(rollbackFor = Exception.class) + public List createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) { + // 1. 校验参数 + knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId()); + + // 2. 下载文档 + List contents = convertList(createListReqVO.getList(), document -> readUrl(document.getUrl())); + + // 3. 文档记录入库 + List documentDOs = new ArrayList<>(createListReqVO.getList().size()); + for (int i = 0; i < createListReqVO.getList().size(); i++) { + AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i); + String content = contents.get(i); + documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class).setKnowledgeId(createListReqVO.getKnowledgeId()) + .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content)) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + } + knowledgeDocumentMapper.insertBatch(documentDOs); + + // 4. 批量创建文档切片(异步) + documentDOs.forEach(documentDO -> + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent())); + return convertList(documentDOs, AiKnowledgeDocumentDO::getId); + } + @Override public PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) { return knowledgeDocumentMapper.selectPage(pageReqVO); @@ -97,17 +120,28 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return knowledgeDocument; } - private org.springframework.core.io.Resource downloadFile(String url) { + private static String readUrl(String url) { + // 下载文件 + ByteArrayResource resource = null; try { byte[] bytes = HttpUtil.downloadBytes(url); if (bytes.length == 0) { throw exception(KNOWLEDGE_DOCUMENT_FILE_EMPTY); } - return new ByteArrayResource(bytes); + resource = new ByteArrayResource(bytes); } catch (Exception e) { - log.error("[downloadFile][url({}) 下载失败]", url, e); - throw new RuntimeException(e); + log.error("[readUrl][url({}) 读取失败]", url, e); + throw exception(KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL); } + + // 读取文件 + TikaDocumentReader loader = new TikaDocumentReader(resource); + List documents = loader.get(); + Document document = CollUtil.getFirst(documents); + if (document == null || StrUtil.isEmpty(document.getText())) { + throw exception(KNOWLEDGE_DOCUMENT_FILE_READ_FAIL); + } + return document.getText(); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 2a8d9afd87..064d373b73 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import org.springframework.scheduling.annotation.Async; import java.util.List; @@ -32,6 +33,17 @@ public interface AiKnowledgeSegmentService { */ void createKnowledgeSegmentBySplitContent(Long documentId, String content); + /** + * 【异步】基于 content 内容,切片创建多个段落 + * + * @param documentId 知识库文档编号 + * @param content 文档内容 + */ + @Async + default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) { + createKnowledgeSegmentBySplitContent(documentId, content); + } + /** * 更新段落的内容 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 9b15135c95..615299b2db 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -110,8 +110,10 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService // 3.1 更新切片 AiKnowledgeSegmentDO segmentDO = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); segmentMapper.updateById(segmentDO); - // 3.2 重新向量化 - writeVectorStore(vectorStore, segmentDO, new Document(segmentDO.getContent())); + // 3.2 重新向量化,必须开启状态 + if (CommonStatusEnum.isEnable(segmentDO.getStatus())) { + writeVectorStore(vectorStore, segmentDO, new Document(segmentDO.getContent())); + } } @Override From 69a27b1ee2024510bd4b8c3bb28a014927cdbbcc Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 28 Feb 2025 14:46:02 +0800 Subject: [PATCH 221/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=96=B0=E5=A2=9E=20Kafka=20=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=A1=A5=E6=A2=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 8 +- yudao-module-iot/yudao-module-iot-biz/pom.xml | 5 + .../dal/dataobject/rule/IotDataBridgeDO.java | 34 +++++ .../AbstractCacheableDataBridgeExecute.java | 2 +- .../databridge/IotDataBridgeExecute.java | 2 +- .../databridge/IotHttpDataBridgeExecute.java | 2 +- .../IotKafkaMQDataBridgeExecute.java | 124 ++++++++++++++++++ .../IotRocketMQDataBridgeExecute.java | 2 +- 8 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 23859c7834..7bdba64627 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -35,6 +35,7 @@ 3.3.3 2.3.2 + 3.3.3 2.2.7 @@ -285,13 +286,16 @@ yudao-spring-boot-starter-mq ${revision} - org.apache.rocketmq rocketmq-spring-boot-starter ${rocketmq-spring.version} - + + org.springframework.kafka + spring-kafka + ${kafka-spring.version} + cn.iocoder.boot diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index ea1dde86fe..1c07e49408 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -81,6 +81,11 @@ rocketmq-spring-boot-starter true + + org.springframework.kafka + spring-kafka + true + org.pf4j diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index 213b0cda10..220edef718 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -170,4 +170,38 @@ public class IotDataBridgeDO extends BaseDO { } + /** + * Kafka 配置 + */ + @Data + public static class KafkaMQConfig implements Config { + + /** + * Kafka 服务器地址 + */ + private String bootstrapServers; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 是否启用 SSL + */ + private Boolean ssl; + + /** + * 生产者组 ID + */ + private String groupId; + /** + * 主题 + */ + private String topic; + + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index 264bce553e..e8fbb0ccb3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -8,7 +8,7 @@ import lombok.extern.slf4j.Slf4j; import java.time.Duration; /** - * 带缓存功能的数据桥接执行器抽象类 + * 带缓存功能的数据桥梁执行器抽象类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index cd00f4f3e7..1617c3b091 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; public interface IotDataBridgeExecute { /** - * 执行数据桥接操作 + * 执行数据桥梁操作 * * @param message 设备消息 * @param dataBridge 数据桥梁 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java index 76f1b793f6..27b8bc6bba 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java @@ -32,7 +32,7 @@ public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥接的类型 == HTTP + // 1.1 校验数据桥梁的类型 == HTTP if (!IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { return; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java new file mode 100644 index 0000000000..8c0ef2b038 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -0,0 +1,124 @@ +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import lombok.extern.slf4j.Slf4j; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * Kafka 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { + + @Override + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥梁的类型 == KAFKA + if (!IotDataBridgTypeEnum.KAFKA.getType().equals(dataBridge.getType())) { + return; + } + // 1.2 执行 Kafka 发送消息 + executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig()); + } + + private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) { + try { + // 1. 获取或创建 KafkaTemplate + KafkaTemplate kafkaTemplate = (KafkaTemplate) getProducer(config); + + // 2. 发送消息并等待结果 + kafkaTemplate.send(config.getTopic(), message.toString()) + .get(10, TimeUnit.SECONDS); // 添加超时等待 + log.info("[executeKafka][message({}) 发送成功]", message); + } catch (TimeoutException e) { + log.error("[executeKafka][message({}) config({}) 发送超时]", message, config, e); + } catch (Exception e) { + log.error("[executeKafka][message({}) config({}) 发送异常]", message, config, e); + } + } + + @Override + protected Object initProducer(Object config) { + IotDataBridgeDO.KafkaMQConfig kafkaConfig = (IotDataBridgeDO.KafkaMQConfig) config; + + // 1.1 构建生产者配置 + Map props = new HashMap<>(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers()); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + + // 1.2 如果配置了认证信息 + if (kafkaConfig.getUsername() != null && kafkaConfig.getPassword() != null) { + props.put("security.protocol", "SASL_PLAINTEXT"); + props.put("sasl.mechanism", "PLAIN"); + props.put("sasl.jaas.config", + "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"" + + kafkaConfig.getUsername() + "\" password=\"" + kafkaConfig.getPassword() + "\";"); + } + + // 1.3 如果启用 SSL + if (Boolean.TRUE.equals(kafkaConfig.getSsl())) { + props.put("security.protocol", "SSL"); + } + + // 2. 创建 KafkaTemplate + DefaultKafkaProducerFactory producerFactory = new DefaultKafkaProducerFactory<>(props); + return new KafkaTemplate<>(producerFactory); + } + + @Override + protected void closeProducer(Object producer) { + if (producer instanceof KafkaTemplate) { + ((KafkaTemplate) producer).destroy(); + } + } + + // TODO @芋艿:测试代码,后续清理 + public static void main(String[] args) { + // 1. 创建一个共享的实例 + IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute(); + + // 2. 创建共享的配置 + IotDataBridgeDO.KafkaMQConfig config = new IotDataBridgeDO.KafkaMQConfig(); + config.setBootstrapServers("127.0.0.1:9092"); + config.setTopic("test-topic"); + config.setSsl(false); + config.setUsername(null); + config.setPassword(null); + + // 3. 创建共享的消息 + IotDeviceMessage message = IotDeviceMessage.builder() + .requestId("TEST-001") + .productKey("testProduct") + .deviceName("testDevice") + .deviceKey("testDeviceKey") + .type("property") + .identifier("temperature") + .data("{\"value\": 60}") + .reportTime(LocalDateTime.now()) + .tenantId(1L) + .build(); + + // 4. 执行两次测试,验证缓存 + log.info("[main][第一次执行,应该会创建新的 producer]"); + action.executeKafka(message, config); + + log.info("[main][第二次执行,应该会复用缓存的 producer]"); + action.executeKafka(message, config); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index af701cd903..3b53252539 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -24,7 +24,7 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥接的类型 == ROCKETMQ + // 1.1 校验数据桥梁的类型 == ROCKETMQ if (!IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) { return; } From 0f0ebda4692da2044ca6b27c44de5de45e5e91da Mon Sep 17 00:00:00 2001 From: alwayssuper <191763414@qq.com> Date: Fri, 28 Feb 2025 15:28:38 +0800 Subject: [PATCH 222/309] =?UTF-8?q?[fix]=EF=BC=9Aiot=20home=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/IotStatisticsController.java | 51 ++++++++------- ...tStatisticsDeviceMessageSummaryRespVO.java | 19 ++++++ .../statistics/vo/IotStatisticsReqVO.java | 8 +-- ...O.java => IotStatisticsSummaryRespVO.java} | 62 +++++-------------- .../iot/dal/mysql/device/IotDeviceMapper.java | 17 +++++ .../dal/mysql/product/IotProductMapper.java | 5 -- .../iot/dal/tdengine/IotDeviceLogMapper.java | 28 +++------ .../iot/service/device/IotDeviceService.java | 26 +++++--- .../service/device/IotDeviceServiceImpl.java | 25 ++++++-- .../device/data/IotDeviceLogService.java | 31 +++++----- .../device/data/IotDeviceLogServiceImpl.java | 56 +++++++++-------- .../product/IotProductCategoryService.java | 3 +- .../IotProductCategoryServiceImpl.java | 54 ++++------------ .../service/product/IotProductService.java | 8 --- .../product/IotProductServiceImpl.java | 4 -- .../mapper/device/IotDeviceLogMapper.xml | 4 +- .../mapper/device/IotDeviceMapper.xml | 25 ++++++++ 17 files changed, 217 insertions(+), 209 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/{IotStatisticsRespVO.java => IotStatisticsSummaryRespVO.java} (59%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index bb5fa92ae8..c4ef83a4ac 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -1,8 +1,9 @@ package cn.iocoder.yudao.module.iot.controller.admin.statistics; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService; @@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; +import java.util.Map; @Tag(name = "管理后台 - IoT 数据统计") @RestController @@ -35,38 +37,41 @@ public class IotStatisticsController { private IotDeviceLogService deviceLogService; // TODO @super:description 非必要,可以不写哈 - @GetMapping("/main") - @Operation(summary = "获取首页的数据统计", description = "用于IOT首页的数据统计") - public CommonResult getIotMainStats(@Valid IotStatisticsReqVO reqVO){ - // TODO @super:新增 get-summary 接口,返回:总数、今日新增、数量、状态 - IotStatisticsRespVO iotStatisticsRespVO = new IotStatisticsRespVO(); + @GetMapping("/get-summary") + @Operation(summary = "获取IOT数据统计") + public CommonResult getIotStatisticsSummary(){ + IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO(); // 获取总数 - iotStatisticsRespVO.setCategoryTotal(productCategoryService.getProductCategoryCount(null)); - iotStatisticsRespVO.setProductTotal(productService.getProductCount(null)); - iotStatisticsRespVO.setDeviceTotal(deviceService.getDeviceCount(null)); - iotStatisticsRespVO.setReportTotal(deviceLogService.getDeviceLogCount(null)); + respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null)); + respVO.setProductCount(productService.getProductCount(null)); + respVO.setDeviceCount(deviceService.getDeviceCount(null)); + respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null)); // 获取今日新增数量 LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); - iotStatisticsRespVO.setCategoryTodayTotal(productCategoryService.getProductCategoryCount(todayStart)); - iotStatisticsRespVO.setProductTodayTotal(productService.getProductCount(todayStart)); - iotStatisticsRespVO.setDeviceTodayTotal(deviceService.getDeviceCount(todayStart)); - iotStatisticsRespVO.setReportTodayTotal(deviceLogService.getDeviceLogCount(todayStart)); + respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart)); + respVO.setProductTodayCount(productService.getProductCount(todayStart)); + respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart)); + respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart)); // 获取各个品类下设备数量统计 - iotStatisticsRespVO.setDeviceStatsOfCategory( - productCategoryService.getDeviceCountsOfProductCategory() - ); + respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap()); // 获取设备状态数量统计 - iotStatisticsRespVO.setOnlineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.ONLINE.getState())); - iotStatisticsRespVO.setOfflineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.OFFLINE.getState())); - iotStatisticsRespVO.setNeverOnlineTotal(deviceService.getDeviceCountByState(IotDeviceStateEnum.INACTIVE.getState())); + Map deviceCountMap = deviceService.getDeviceCountMapByState(); + respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L)); + respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L)); + respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L)); - // TODO @super:新增 get-log-summary 接口,返回 + return CommonResult.success(respVO); + } + @GetMapping("/get-log-summary") + @Operation(summary = "获取IOT上下行消息数据统计") + public CommonResult getIotStatisticsDeviceMessageSummary(@Valid IotStatisticsReqVO reqVO){ // 根据传入时间范围获取设备上下行消息数量统计 - iotStatisticsRespVO.setDeviceUpMessageStats(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); - iotStatisticsRespVO.setDeviceDownMessageStats(deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); + IotStatisticsDeviceMessageSummaryRespVO iotStatisticsRespVO = new IotStatisticsDeviceMessageSummaryRespVO(); + iotStatisticsRespVO.setUpstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); + iotStatisticsRespVO.setDownstreamCounts(deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); return CommonResult.success(iotStatisticsRespVO); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java new file mode 100644 index 0000000000..250c63e2ab --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - Iot 上下行消息数量统计 Response VO") +@Data +public class IotStatisticsDeviceMessageSummaryRespVO { + @Schema(description = "每小时上行数据数量统计") + private List> upstreamCounts; + + @Schema(description = "每小时下行数据数量统计") + private List> downstreamCounts; + + // TODO @super:如果只有这两个字段,使用 KeyValue 这个键值对 +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java index 4a225bc5a5..acffe1299f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java @@ -11,12 +11,12 @@ public class IotStatisticsReqVO { // TODO @supper:times 直接传递哈; // TODO 2super:private 不要丢了 - @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000") @NotNull(message = "查询起始时间不能为空") - Long startTime; + private Long startTime; - @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") + @Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1758486600000") @NotNull(message = "查询结束时间不能为空") - Long endTime; + private Long endTime; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java similarity index 59% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java index 0e68c078d0..0ad1f9ff4f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import java.util.List; +import java.util.Map; // TODO @super:Total 全部改成 Count // TODO @super:IotStatisticsSummaryRespVO @@ -12,90 +12,58 @@ import java.util.List; */ @Schema(description = "管理后台 - Iot 统计 Response VO") @Data -public class IotStatisticsRespVO { +public class IotStatisticsSummaryRespVO { // TODO @super:productCategory 哈 @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private long categoryTotal; + private long productCategoryCount; @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private long productTotal; + private long productCount; @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private long deviceTotal; + private long deviceCount; // TODO @super:deviceMessageCount;设备消息数量 @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - private long reportTotal; + private long deviceMessageCount; // TODO @super:productCategory 哈 @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private long categoryTodayTotal; + private long productCategoryTodayCount; @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private long productTodayTotal; + private long productTodayCount; @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private long deviceTodayTotal; + private long deviceTodayCount; // TODO @super:deviceMessageCount;今日设备消息数量 @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - private long reportTodayTotal; + private long deviceMessageTodayCount; // TODO @super:deviceOnlineCount @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") - private long onlineTotal; + private long deviceOnlineCount; // TODO @super:deviceOfflineCount @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") - private long offlineTotal; + private long deviceOfflineCount; // TODO @super:deviceInactivECount @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - private long neverOnlineTotal; + private long deviceInactiveCount; // TODO @super:1)类型改成 Map,key 分类名、value 设备数量;2)deviceStatsOfCategory => productCategoryDeviceCounts @Schema(description = "按品类统计的设备数量") - private List deviceStatsOfCategory; + private Map productCategoryDeviceCounts; // TODO @super:貌似界面里,用不到这个字段??? - @Schema(description = "上报数据数量统计") - private List reportDataStats; + // TODO @super:deviceUpMessageStats、deviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO,然后里面属性就是 upstreamCounts、downstreamCounts - @Schema(description = "上行数据数量统计") - private List deviceUpMessageStats; - - @Schema(description = "下行数据数量统计") - private List deviceDownMessageStats; - - // TODO @super:如果只有这两个字段,使用 KeyValue 这个键值对 - @Schema(description = "时间数据") - @Data - public static class TimeData { - - @Schema(description = "时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1646092800000") - private long time; - - @Schema(description = "数据值", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private Object data; - - } - - @Schema(description = "数据项") - @Data - public static class DataItem { - - @Schema(description = "数据项名", requiredMode = Schema.RequiredMode.REQUIRED, example = "智能家居") - private String name; - - @Schema(description = "数据项值", requiredMode = Schema.RequiredMode.REQUIRED, example = "50") - private Object value; - - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index cceeb53385..128b026f50 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -8,10 +8,13 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * IoT 设备 Mapper @@ -81,4 +84,18 @@ public interface IotDeviceMapper extends BaseMapperX { .eqIfPresent(IotDeviceDO::getState, state)); } + /** + * 查询指定产品下各状态的设备数量 + * + * @return 设备数量统计列表 + */ + List> selectDeviceCountMapByProductId(); + + /** + * 查询各个状态下的设备数量 + * + * @return 设备数量统计列表 + */ + List> selectDeviceCountGroupByState(); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java index f63a14be95..5ba4a81772 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java @@ -37,10 +37,5 @@ public interface IotProductMapper extends BaseMapperX { .geIfPresent(IotProductDO::getCreateTime, createTime)); } - default List selectListByCategoryId(Long categoryId) { - return selectList(new LambdaQueryWrapperX() - .eq(IotProductDO::getCategoryId, categoryId) - .orderByDesc(IotProductDO::getId)); - } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java index df64aefb30..9efe2ec805 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.tdengine; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.framework.tdengine.core.annotation.TDengineDS; import com.baomidou.mybatisplus.annotation.InterceptorIgnore; @@ -10,6 +9,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; +import java.util.Map; /** * 设备日志 {@link IotDeviceLogDO} Mapper 接口 @@ -60,27 +60,17 @@ public interface IotDeviceLogMapper { // TODO @super:1)上行、下行,不写在 mapper 里,而是通过参数传递,这样,selectDeviceLogUpCountByHour、selectDeviceLogDownCountByHour 可以合并; // TODO @super:2)不能只基于 identifier 来计算,而是要 type + identifier 成对 /** - * 获得每个小时设备上行消息数量统计 - * - * @param deviceKey 设备标识 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 每小时消息数量统计 + * 查询每个小时设备上行消息数量 */ - List selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime); + List> selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey, + @Param("startTime") Long startTime, + @Param("endTime") Long endTime); /** - * 获得每个小时设备下行消息数量统计 - * - * @param deviceKey 设备标识 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 每小时消息数量统计 + * 查询每个小时设备下行消息数量 */ - List selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime); + List> selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey, + @Param("startTime") Long startTime, + @Param("endTime") Long endTime); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 6d48b38d29..0e50f4a27a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; @@ -10,6 +11,7 @@ import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; +import java.util.Map; /** * IoT 设备 Service 接口 @@ -111,7 +113,7 @@ public interface IotDeviceService { IotDeviceDO getDeviceByDeviceKey(String deviceKey); /** - * ��得设备分页 + * 获得设备分页 * * @param pageReqVO 分页查询 * @return IoT 设备分页 @@ -194,13 +196,6 @@ public interface IotDeviceService { */ Long getDeviceCount(@Nullable LocalDateTime createTime); - /** - * 获得设备数量,基于状态 - * - * @param state 状态 - * @return 设备数量 - */ - Long getDeviceCountByState(Integer state); /** * 获得所有设备列表 @@ -217,4 +212,19 @@ public interface IotDeviceService { */ IotDeviceMqttConnectionParamsRespVO getMqttConnectionParams(Long deviceId); + /** + * 获得各个产品下的设备数量 Map + * + * @return key: 产品编号, value: 设备数量 + */ + Map getDeviceCountMapByProductId(); + + /** + * 获得各个状态下的设备数量 Map + * + * @return key: 设备状态枚举 {@link IotDeviceStateEnum} + * value: 设备数量 + */ + Map getDeviceCountMapByState(); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index 4d4892733b..b6c0969d17 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -35,6 +35,7 @@ import org.springframework.validation.annotation.Validated; import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.*; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -430,14 +431,30 @@ public class IotDeviceServiceImpl implements IotDeviceService { } // TODO @super:是不是 groupby 查询,更高效;不过 controller,还是要考虑 null 的情况;不过可以直接枚举 foreach 处理下 - @Override - public Long getDeviceCountByState(Integer state) { - return deviceMapper.selectCountByState(state); - } @Override public List getDeviceList() { return deviceMapper.selectList(); } + @Override + public Map getDeviceCountMapByProductId() { + // 查询结果转换成Map + List> list = deviceMapper.selectDeviceCountMapByProductId(); + return list.stream().collect(Collectors.toMap( + map -> Long.valueOf(map.get("key").toString()), + map -> Integer.valueOf(map.get("value").toString()) + )); + } + + @Override + public Map getDeviceCountMapByState() { + // 查询结果转换成Map + List> list = deviceMapper.selectDeviceCountGroupByState(); + return list.stream().collect(Collectors.toMap( + map -> Integer.valueOf(map.get("key").toString()), + map -> Long.valueOf(map.get("value").toString()) + )); + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java index 592217bb60..e1a15aaa16 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java @@ -2,13 +2,14 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; /** * IoT 设备日志数据 Service 接口 @@ -50,25 +51,25 @@ public interface IotDeviceLogService { /** * 获得每个小时设备上行消息数量统计 * - * @param deviceKey 设备标识,如果为空,则统计所有设备 - * @param startTime 开始时间,如果为空,则不限制开始时间 - * @param endTime 结束时间,如果为空,则不限制结束时间 - * @return 每小时消息数量统计列表 + * @param deviceKey 设备标识 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return key: 时间戳, value: 消息数量 */ - List getDeviceLogUpCountByHour(@Nullable String deviceKey, - @Nullable Long startTime, - @Nullable Long endTime); + List> getDeviceLogUpCountByHour(@Nullable String deviceKey, + @Nullable Long startTime, + @Nullable Long endTime); /** * 获得每个小时设备下行消息数量统计 * - * @param deviceKey 设备标识,如果为空,则统计所有设备 - * @param startTime 开始时间,如果为空,则不限制开始时间 - * @param endTime 结束时间,如果为空,则不限制结束时间 - * @return 每小时消息数量统计列表 + * @param deviceKey 设备标识 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return key: 时间戳, value: 消息数量 */ - List getDeviceLogDownCountByHour(@Nullable String deviceKey, - @Nullable Long startTime, - @Nullable Long endTime); + List> getDeviceLogDownCountByHour(@Nullable String deviceKey, + @Nullable Long startTime, + @Nullable Long endTime); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java index a5ac4a8c1b..29b57402b5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -1,12 +1,13 @@ package cn.iocoder.yudao.module.iot.service.device.data; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; @@ -17,10 +18,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.sql.Timestamp; import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * IoT 设备日志数据 Service 实现类 @@ -71,37 +73,39 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { @Override public Long getDeviceLogCount(LocalDateTime createTime) { - Long time = null; - if (createTime != null) { // todo @super:1)LocalDateTimeUtil.toEpochMilli(createTime);2)直接表达式,更简洁 time != null ? createTime.toInstant(ZoneOffset.UTC).toEpochMilli() : null; - time = createTime.toInstant(ZoneOffset.UTC).toEpochMilli(); - } - return deviceLogMapper.selectCountByCreateTime(time); + return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); } // TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤 @Override - public List getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { - try { - return deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime); - } catch (Exception exception) { - if (exception.getMessage().contains("Table does not exist")) { - return new ArrayList<>(); - } - throw exception; - } + public List> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { + List> list = deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime); + return list.stream() + .map(map -> { + // 从Timestamp获取时间戳 + Timestamp timestamp = (Timestamp) map.get("time"); + Long timeMillis = timestamp.getTime(); + // 消息数量转换 + Integer count = ((Number) map.get("data")).intValue(); + return Map.of(timeMillis, count); + }) + .collect(Collectors.toList()); } @Override - public List getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { - try { - return deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime); - } catch (Exception exception) { - if (exception.getMessage().contains("Table does not exist")) { - return new ArrayList<>(); - } - throw exception; - } + public List> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { + List> list = deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime); + return list.stream() + .map(map -> { + // 从Timestamp获取时间戳 + Timestamp timestamp = (Timestamp) map.get("time"); + Long timeMillis = timestamp.getTime(); + // 消息数量转换 + Integer count = ((Number) map.get("data")).intValue(); + return Map.of(timeMillis, count); + }) + .collect(Collectors.toList()); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java index 8d6ccdadaa..662d161c10 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.product; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import jakarta.validation.Valid; @@ -100,6 +99,6 @@ public interface IotProductCategoryService { * * @return 品类设备统计列表 */ - List getDeviceCountsOfProductCategory(); + Map getProductCategoryDeviceCountMap(); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index d8b9af1fb3..5499937fde 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -5,8 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsRespVO; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductCategoryMapper; @@ -16,11 +14,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEGORY_NOT_EXISTS; @@ -103,54 +97,30 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService } @Override - public List getDeviceCountsOfProductCategory() { + public Map getProductCategoryDeviceCountMap() { // 1. 获取所有数据 List categoryList = productCategoryMapper.selectList(); List productList = productService.getProductList(); // TODO @super:不要 list 查询,返回内存,而是查询一个 Map - List deviceList = deviceService.getDeviceList(); + Map deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId(); // 2. 统计每个分类下的设备数量 Map categoryDeviceCountMap = new HashMap<>(); - + // 2.1 初始化所有分类的计数为0 for (IotProductCategoryDO category : categoryList) { categoryDeviceCountMap.put(category.getName(), 0); - // TODO @super:直接这里面计算,不用多个循环。产品本身也不多,不用构建 Map,直接 filter 就好了 - } - - // 2.2 构建产品ID到分类的映射 - Map productCategoryMap = new HashMap<>(); - for (IotProductDO product : productList) { - Long categoryId = product.getCategoryId(); - IotProductCategoryDO category = categoryList.stream() - .filter(c -> c.getId().equals(categoryId)) - .findFirst() - .orElse(null); - if (category != null) { - productCategoryMap.put(product.getId(), category); + + // 2.2 找到该分类下的所有产品,累加设备数量 + for (IotProductDO product : productList) { + if (Objects.equals(product.getCategoryId(), category.getId())) { + Integer deviceCount = deviceCountMapByProductId.getOrDefault(product.getId(), 0); + categoryDeviceCountMap.merge(category.getName(), deviceCount, Integer::sum); + } } } - // 2.3 统计每个分类下的设备数量 - for (IotDeviceDO device : deviceList) { - Long productId = device.getProductId(); - IotProductCategoryDO category = productCategoryMap.get(productId); - if (category != null) { - String categoryName = category.getName(); - categoryDeviceCountMap.merge(categoryName, 1, Integer::sum); - } - } - - // 3. 转换为 DataItem 列表 - return categoryDeviceCountMap.entrySet().stream() - .map(entry -> { - IotStatisticsRespVO.DataItem dataItem = new IotStatisticsRespVO.DataItem(); - dataItem.setName(entry.getKey()); - dataItem.setValue(entry.getValue()); - return dataItem; - }) - .collect(Collectors.toList()); + return categoryDeviceCountMap; } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java index 5d6f7c788a..8497d73aa9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java @@ -102,13 +102,5 @@ public interface IotProductService { */ Long getProductCount(@Nullable LocalDateTime createTime); - // TODO @super:用不到的,删除下哈 - /** - * 获得产品列表,基于分类编号 - * - * @param categoryId 分类编号 - * @return 产品列表 - */ - List getProductListByCategoryId(Long categoryId); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java index 848da74d65..4a7263c27b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java @@ -143,9 +143,5 @@ public class IotProductServiceImpl implements IotProductService { return productMapper.selectCountByCreateTime(createTime); } - @Override - public List getProductListByCategoryId(Long categoryId) { - return productMapper.selectListByCategoryId(categoryId); - } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml index 4a4c3d6bde..932a9a862c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceLogMapper.xml @@ -65,7 +65,7 @@ - SELECT TIMETRUNCATE(ts, 1h) as time, COUNT(*) as data @@ -93,7 +93,7 @@ ORDER BY time ASC - SELECT TIMETRUNCATE(ts, 1h) as time, COUNT(*) as data diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml new file mode 100644 index 0000000000..8404729cce --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ No newline at end of file From 7b449b81e7be2671446a94a74c9c539f6c9be2a8 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 28 Feb 2025 18:03:34 +0800 Subject: [PATCH 223/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=96=B0=E5=A2=9E=20RabbitMQ=20?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 7 + yudao-module-iot/yudao-module-iot-biz/pom.xml | 5 + .../dal/dataobject/rule/IotDataBridgeDO.java | 41 ++++++ .../IotRabbitMQDataBridgeExecute.java | 126 ++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 7bdba64627..fa54eef606 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -36,6 +36,7 @@ 2.3.2 3.3.3 + 3.4.3 2.2.7 @@ -296,6 +297,12 @@ spring-kafka ${kafka-spring.version} + + org.springframework.boot + spring-boot-starter-amqp + ${rabbitmq-spring.version} + + cn.iocoder.boot diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 1c07e49408..1b897c5d7f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -86,6 +86,11 @@ spring-kafka true + + org.springframework.boot + spring-boot-starter-amqp + true + org.pf4j diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index 220edef718..e95cb695ee 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -204,4 +204,45 @@ public class IotDataBridgeDO extends BaseDO { } + /** + * RabbitMQ 配置 + */ + @Data + public static class RabbitMQConfig implements Config { + + /** + * RabbitMQ 服务器地址 + */ + private String host; + /** + * 端口 + */ + private Integer port; + /** + * 虚拟主机 + */ + private String virtualHost; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + + /** + * 交换机名称 + */ + private String exchange; + /** + * 路由键 + */ + private String routingKey; + /** + * 队列名称 + */ + private String queue; + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java new file mode 100644 index 0000000000..2c257051f6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -0,0 +1,126 @@ +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; + +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; + +/** + * RabbitMQ 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { + + @Override + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥梁的类型 == RABBITMQ + if (!IotDataBridgTypeEnum.RABBITMQ.getType().equals(dataBridge.getType())) { + return; + } + // 1.2 执行 RabbitMQ 发送消息 + executeRabbitMQ(message, (IotDataBridgeDO.RabbitMQConfig) dataBridge.getConfig()); + } + + private void executeRabbitMQ(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) { + try { + // 1. 获取或创建 Channel + Channel channel = (Channel) getProducer(config); + + // 2.1 声明交换机、队列和绑定关系 + channel.exchangeDeclare(config.getExchange(), "direct", true); + channel.queueDeclare(config.getQueue(), true, false, false, null); + channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); + + // 2.2 发送消息 + channel.basicPublish(config.getExchange(), config.getRoutingKey(), null, + message.toString().getBytes(StandardCharsets.UTF_8)); + log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config); + } catch (Exception e) { + log.error("[executeRabbitMQ][message({}) config({}) 发送异常]", message, config, e); + } + } + + @Override + protected Object initProducer(Object config) throws Exception { + IotDataBridgeDO.RabbitMQConfig rabbitConfig = (IotDataBridgeDO.RabbitMQConfig) config; + + // 1. 创建连接工厂 + ConnectionFactory factory = new ConnectionFactory(); + factory.setHost(rabbitConfig.getHost()); + factory.setPort(rabbitConfig.getPort()); + factory.setVirtualHost(rabbitConfig.getVirtualHost()); + factory.setUsername(rabbitConfig.getUsername()); + factory.setPassword(rabbitConfig.getPassword()); + + // 2. 创建连接 + Connection connection = factory.newConnection(); + + // 3. 创建信道 + return connection.createChannel(); + } + + @Override + protected void closeProducer(Object producer) { + if (producer instanceof Channel) { + try { + Channel channel = (Channel) producer; + if (channel.isOpen()) { + channel.close(); + } + Connection connection = channel.getConnection(); + if (connection.isOpen()) { + connection.close(); + } + } catch (Exception e) { + log.error("[closeProducer][关闭 RabbitMQ 连接异常]", e); + } + } + } + + // TODO @芋道源码:测试代码,后续清理 + public static void main(String[] args) { + // 1. 创建一个共享的实例 + IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute(); + + // 2. 创建共享的配置 + IotDataBridgeDO.RabbitMQConfig config = new IotDataBridgeDO.RabbitMQConfig(); + config.setHost("localhost"); + config.setPort(5672); + config.setVirtualHost("/"); + config.setUsername("admin"); + config.setPassword("123456"); + config.setExchange("test-exchange"); + config.setRoutingKey("test-key"); + config.setQueue("test-queue"); + + // 3. 创建共享的消息 + IotDeviceMessage message = IotDeviceMessage.builder() + .requestId("TEST-001") + .productKey("testProduct") + .deviceName("testDevice") + .deviceKey("testDeviceKey") + .type("property") + .identifier("temperature") + .data("{\"value\": 60}") + .reportTime(LocalDateTime.now()) + .tenantId(1L) + .build(); + + // 4. 执行两次测试,验证缓存 + log.info("[main][第一次执行,应该会创建新的 channel]"); + action.executeRabbitMQ(message, config); + + log.info("[main][第二次执行,应该会复用缓存的 channel]"); + action.executeRabbitMQ(message, config); + } +} From e5cc9d2ad857939a42a8ddabd37c9c445981d734 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 28 Feb 2025 18:57:17 +0800 Subject: [PATCH 224/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91AI=EF=BC=9A=E7=9F=A5=E8=AF=86=E5=BA=93?= =?UTF-8?q?=E7=9A=84=E7=B4=A2=E5=BC=95=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledge/AiKnowledgeController.java | 12 +++++- .../AiKnowledgeDocumentController.java | 10 ++++- .../AiKnowledgeSegmentController.java | 1 + .../AiKnowledgeDocumentCreateListReqVO.java | 10 ++--- .../AiKnowledgeDocumentPageReqVO.java | 3 ++ .../document/AiKnowledgeDocumentRespVO.java | 39 +++++++++++-------- .../AiKnowledgeDocumentUpdateReqVO.java | 4 +- .../vo/knowledge/AiKnowledgePageReqVO.java | 17 +++++++- .../vo/knowledge/AiKnowledgeRespVO.java | 21 ++++++++-- .../vo/knowledge/AiKnowledgeSaveReqVO.java | 7 +++- .../knowledge/AiKnowledgeDocumentDO.java | 9 ++++- .../knowledge/AiKnowledgeSegmentDO.java | 5 +++ .../knowledge/AiKnowledgeDocumentMapper.java | 3 +- .../mysql/knowledge/AiKnowledgeMapper.java | 10 +++-- .../knowledge/AiKnowledgeSegmentMapper.java | 2 +- .../knowledge/AiKnowledgeDocumentService.java | 10 ++++- .../AiKnowledgeDocumentServiceImpl.java | 17 +++++--- .../service/knowledge/AiKnowledgeService.java | 8 ++++ .../knowledge/AiKnowledgeServiceImpl.java | 12 ++++-- 19 files changed, 151 insertions(+), 49 deletions(-) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/{knowledge => document}/AiKnowledgeDocumentCreateListReqVO.java (84%) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index 5c27810260..df49395750 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +// TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库") @RestController @RequestMapping("/ai/knowledge") @@ -29,11 +31,19 @@ public class AiKnowledgeController { @GetMapping("/page") @Operation(summary = "获取知识库分页") public CommonResult> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { - + PageResult pageResult = knowledgeService.getKnowledgePage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); } + @GetMapping("/get") + @Operation(summary = "获得知识库") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + public CommonResult getKnowledge(@RequestParam("id") Long id) { + AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id); + return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class)); + } + @PostMapping("/create") @Operation(summary = "创建知识库") public CommonResult createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index 8d61d0c1e4..12e8ad1fcd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateListReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; @@ -21,6 +21,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +// TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库文档") @RestController @RequestMapping("/ai/knowledge/document") @@ -38,6 +39,13 @@ public class AiKnowledgeDocumentController { return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class)); } + @GetMapping("/get") + @Operation(summary = "获取文档详情") + public CommonResult getKnowledgeDocument(@RequestParam("id") Long id) { + AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id); + return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class)); + } + @PostMapping("/create") @Operation(summary = "新建文档") public CommonResult createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index d4ca7ca499..267f0021d2 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +// TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库段落") @RestController @RequestMapping("/ai/knowledge/segment") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java similarity index 84% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java index 4bd817aa4c..6545c0bc1c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateListReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; @@ -17,6 +17,10 @@ public class AiKnowledgeDocumentCreateListReqVO { @NotNull(message = "知识库编号不能为空") private Long knowledgeId; + @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") + @NotNull(message = "分段的最大 Token 数不能为空") + private Integer segmentMaxTokens; + @Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "文档列表不能为空") private List list; @@ -33,10 +37,6 @@ public class AiKnowledgeDocumentCreateListReqVO { @URL(message = "文档 URL 格式不正确") private String url; - @Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800") - @NotNull(message = "分段的最大 Token 数不能为空") - private Integer segmentMaxTokens; - } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java index 76c001bd35..15bb603c21 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java @@ -8,6 +8,9 @@ import lombok.Data; @Data public class AiKnowledgeDocumentPageReqVO extends PageParam { + @Schema(description = "知识库编号", example = "1") + private Long knowledgeId; + @Schema(description = "文档名称", example = "Java 开发手册") private String name; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java index 96ca61b3d2..7aef94a3f4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java @@ -1,38 +1,45 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; -import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - AI 知识库-文档 Response VO") -@Data -public class AiKnowledgeDocumentRespVO extends PageParam { +import java.time.LocalDateTime; - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") +@Schema(description = "管理后台 - AI 知识库文档 Response VO") +@Data +public class AiKnowledgeDocumentRespVO { + + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") private Long id; @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") private Long knowledgeId; - @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") private String name; - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....") - private String content; - - @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") + @Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn") private String url; - @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....") + private String content; + + @Schema(description = "文档内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Integer contentLength; + + @Schema(description = "文档 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Integer tokens; - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") - private Integer wordCount; + @Schema(description = "分片最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "512") + private Integer segmentMaxTokens; - @Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer sliceStatus; + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer retrievalCount; - @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") private Integer status; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java index 13f871f6d4..1097f40606 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java @@ -6,8 +6,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; - -@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO") +// TODO @芋艿:稍后优化 +@Schema(description = "管理后台 - AI 知识库文档更新 Request VO") @Data public class AiKnowledgeDocumentUpdateReqVO { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java index 941732f1ad..dc7943cf25 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java @@ -1,14 +1,29 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - AI 知识库的分页 Request VO") @Data public class AiKnowledgePageReqVO extends PageParam { - @Schema(description = "知识库名称", example = "Java 开发手册") + @Schema(description = "知识库名称", example = "芋艿") private String name; + @Schema(description = "是否启用", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java index 3ff8a1c757..5e83b85a72 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import java.time.LocalDateTime; @Schema(description = "管理后台 - AI 知识库 Response VO") @Data @@ -17,10 +18,22 @@ public class AiKnowledgeRespVO { @Schema(description = "知识库描述", example = "帮助你快速构建系统") private String description; - @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") - private Long modelId; + @Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14") + private Long embeddingModelId; - @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat") - private String model; + @Schema(description = "向量模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat") + private String embeddingModel; + + @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") + private Integer topK; + + @Schema(description = "相似度阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7") + private Double similarityThreshold; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java index f7c6a245ca..774b7234a7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -31,6 +33,9 @@ public class AiKnowledgeSaveReqVO { @NotNull(message = "相似性阈值不能为空") private Double similarityThreshold; - // TODO @芋艿:status + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "是否启用不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java index 56de374f7e..ac014e926b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java @@ -29,7 +29,7 @@ public class AiKnowledgeDocumentDO extends BaseDO { */ private Long knowledgeId; /** - * 文件名称 + * 文档名称 */ private String name; /** @@ -50,10 +50,15 @@ public class AiKnowledgeDocumentDO extends BaseDO { */ private Integer tokens; /** - * 每个文本块的目标 token 数 + * 分片最大 Token 数 */ private Integer segmentMaxTokens; + /** + * 召回次数 + */ + private Integer retrievalCount; + /** * 状态 *

diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java index 5ab18e7d7a..cccbd6846b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java @@ -57,6 +57,11 @@ public class AiKnowledgeSegmentDO extends BaseDO { */ private Integer tokens; + /** + * 召回次数 + */ + private Integer retrievalCount; + /** * 状态 *

diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java index 7692d1cede..8e6bb4d2b7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO import org.apache.ibatis.annotations.Mapper; /** - * AI 知识库-文档 Mapper + * AI 知识库文档 Mapper * * @author xiaoxin */ @@ -17,6 +17,7 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX selectPage(AiKnowledgeDocumentPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(AiKnowledgeDocumentDO::getKnowledgeId, reqVO.getKnowledgeId()) .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName()) .orderByDesc(AiKnowledgeDocumentDO::getId)); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java index 735332beb9..0d6022ed07 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -9,7 +8,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import org.apache.ibatis.annotations.Mapper; /** - * AI 知识库基础信息 Mapper + * AI 知识库 Mapper * * @author xiaoxin */ @@ -18,7 +17,10 @@ public interface AiKnowledgeMapper extends BaseMapperX { default PageResult selectPage(AiKnowledgePageReqVO pageReqVO) { return selectPage(pageReqVO, new LambdaQueryWrapperX() - .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) - .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())); + .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName()) + .eqIfPresent(AiKnowledgeDO::getStatus, pageReqVO.getStatus()) + .betweenIfPresent(AiKnowledgeDO::getCreateTime, pageReqVO.getCreateTime()) + .orderByDesc(AiKnowledgeDO::getId)); } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index 094f19b52e..30b38f6c8c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; /** - * AI 知识库-分片 Mapper + * AI 知识库分片 Mapper * * @author xiaoxin */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 41fa41527d..5d5c811bb2 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateListReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import java.util.List; @@ -40,6 +40,14 @@ public interface AiKnowledgeDocumentService { */ PageResult getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO); + /** + * 获取文档详情 + * + * @param id 文档编号 + * @return 文档详情 + */ + AiKnowledgeDocumentDO getKnowledgeDocument(Long id); + /** * 更新文档 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index fd45b97062..c90839f556 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateListReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; @@ -84,15 +84,17 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic for (int i = 0; i < createListReqVO.getList().size(); i++) { AiKnowledgeDocumentCreateListReqVO.Document documentVO = createListReqVO.getList().get(i); String content = contents.get(i); - documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class).setKnowledgeId(createListReqVO.getKnowledgeId()) - .setContent(content).setContentLength(content.length()).setTokens(tokenCountEstimator.estimate(content)) + documentDOs.add(BeanUtils.toBean(documentVO, AiKnowledgeDocumentDO.class) + .setKnowledgeId(createListReqVO.getKnowledgeId()) + .setContent(content).setContentLength(content.length()) + .setTokens(tokenCountEstimator.estimate(content)) .setStatus(CommonStatusEnum.ENABLE.getStatus())); } knowledgeDocumentMapper.insertBatch(documentDOs); // 4. 批量创建文档切片(异步) - documentDOs.forEach(documentDO -> - knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent())); + documentDOs.forEach(documentDO -> knowledgeSegmentService + .createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), documentDO.getContent())); return convertList(documentDOs, AiKnowledgeDocumentDO::getId); } @@ -101,6 +103,11 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return knowledgeDocumentMapper.selectPage(pageReqVO); } + @Override + public AiKnowledgeDocumentDO getKnowledgeDocument(Long id) { + return knowledgeDocumentMapper.selectById(id); + } + @Override public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { // 1. 校验文档是否存在 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java index 5c454383f9..d1a7f7d1c4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java @@ -27,6 +27,14 @@ public interface AiKnowledgeService { */ void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO); + /** + * 获得知识库 + * + * @param id 编号 + * @return 知识库 + */ + AiKnowledgeDO getKnowledge(Long id); + /** * 校验知识库是否存在 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 583eabaeaa..900cfb4a17 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.ai.service.knowledge; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; @@ -8,11 +7,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.vectorstore.VectorStore; import org.springframework.stereotype.Service; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -40,7 +37,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { // 2. 插入知识库 AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) - .setEmbeddingModel(model.getModel()).setStatus(CommonStatusEnum.ENABLE.getStatus()); + .setEmbeddingModel(model.getModel()); knowledgeMapper.insert(knowledge); return knowledge.getId(); } @@ -56,6 +53,13 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class) .setEmbeddingModel(model.getModel()); knowledgeMapper.updateById(updateObj); + + // TODO @芋艿:如果模型变化,需要 reindex 所有的文档 + } + + @Override + public AiKnowledgeDO getKnowledge(Long id) { + return knowledgeMapper.selectById(id); } @Override From d1e207899a1b7e4497104cd3321f4d2fcf5c3a4c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 07:48:16 +0800 Subject: [PATCH 225/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E7=9F=A5=E8=AF=86=E5=BA=93?= =?UTF-8?q?=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=88=87=E7=89=87=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeSegmentController.http | 5 +++ .../AiKnowledgeSegmentController.java | 23 +++++++++++-- .../vo/segment/AiKnowledgeSegmentRespVO.java | 12 +++++-- .../knowledge/AiKnowledgeDocumentService.java | 8 +++++ .../AiKnowledgeDocumentServiceImpl.java | 5 +-- .../knowledge/AiKnowledgeSegmentService.java | 13 ++++++-- .../AiKnowledgeSegmentServiceImpl.java | 32 ++++++++++++++++--- 7 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http new file mode 100644 index 0000000000..25d622d4c1 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http @@ -0,0 +1,5 @@ +### 切片内容 +GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index 267f0021d2..6177f5411a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -10,12 +10,16 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; // TODO @芋艿:增加权限标识 @@ -30,7 +34,8 @@ public class AiKnowledgeSegmentController { @GetMapping("/page") @Operation(summary = "获取段落分页") - public CommonResult> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) { + public CommonResult> getKnowledgeSegmentPage( + @Valid AiKnowledgeSegmentPageReqVO pageReqVO) { PageResult pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); } @@ -44,9 +49,23 @@ public class AiKnowledgeSegmentController { @PutMapping("/update-status") @Operation(summary = "启禁用段落内容") - public CommonResult updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { + public CommonResult updateKnowledgeSegmentStatus( + @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { segmentService.updateKnowledgeSegmentStatus(reqVO); return success(true); } + @GetMapping("/split") + @Operation(summary = "切片内容") + @Parameters({ + @Parameter(name = "url", description = "文档 URL", required = true), + @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true) + }) + public CommonResult> splitContent( + @RequestParam("url") String url, + @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) { + List segments = segmentService.splitContent(url, segmentMaxTokens); + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class)); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java index 5e3f2d8cbb..24c452621a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - AI 知识库-文档 Response VO") +@Schema(description = "管理后台 - AI 知识库文档分片 Response VO") @Data public class AiKnowledgeSegmentRespVO { @@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO { @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") private String content; + @Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer contentLength; + @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Integer tokens; - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008") - private Integer wordCount; + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer retrievalCount; @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer status; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private Long createTime; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 5d5c811bb2..8bbee98179 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -63,4 +63,12 @@ public interface AiKnowledgeDocumentService { */ AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id); + /** + * 读取 URL 内容 + * + * @param url URL + * @return 内容 + */ + String readUrl(String url); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index c90839f556..6ed7fdd57d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -127,9 +127,10 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return knowledgeDocument; } - private static String readUrl(String url) { + @Override + public String readUrl(String url) { // 下载文件 - ByteArrayResource resource = null; + ByteArrayResource resource; try { byte[] bytes = HttpUtil.downloadBytes(url); if (bytes.length == 0) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 064d373b73..b105dcb8eb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -29,7 +29,7 @@ public interface AiKnowledgeSegmentService { * 基于 content 内容,切片创建多个段落 * * @param documentId 知识库文档编号 - * @param content 文档内容 + * @param content 文档内容 */ void createKnowledgeSegmentBySplitContent(Long documentId, String content); @@ -37,7 +37,7 @@ public interface AiKnowledgeSegmentService { * 【异步】基于 content 内容,切片创建多个段落 * * @param documentId 知识库文档编号 - * @param content 文档内容 + * @param content 文档内容 */ @Async default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) { @@ -66,4 +66,13 @@ public interface AiKnowledgeSegmentService { */ List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO); + /** + * 根据 URL 内容,切片创建多个段落 + * + * @param url URL 地址 + * @param segmentMaxTokens 段落最大 Token 数 + * @return 切片后的段落列表 + */ + List splitContent(String url, Integer segmentMaxTokens); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 615299b2db..37599b01fc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -75,9 +75,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService VectorStore vectorStore = getVectorStoreById(knowledgeDO); // 2. 文档切片 - Document document = new Document(content); - TextSplitter textSplitter = buildTokenTextSplitter(documentDO.getSegmentMaxTokens()); - List documentSegments = textSplitter.apply(Collections.singletonList(document)); + List documentSegments = splitContentByToken(content, documentDO.getSegmentMaxTokens()); // 3.1 存储切片 List segmentDOs = convertList(documentSegments, segment -> { @@ -86,7 +84,8 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId) .setContent(segment.getText()).setContentLength(segment.getText().length()) - .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY).setTokens(tokenCountEstimator.estimate(segment.getText())) + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY) + .setTokens(tokenCountEstimator.estimate(segment.getText())) .setStatus(CommonStatusEnum.ENABLE.getStatus()); }); segmentMapper.insertBatch(segmentDOs); @@ -180,6 +179,26 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService return segmentMapper.selectListByVectorIds(convertList(documents, Document::getId)); } + @Override + public List splitContent(String url, Integer segmentMaxTokens) { + // 1. 读取 URL 内容 + String content = knowledgeDocumentService.readUrl(url); + + // 2. 文档切片 + List documentSegments = splitContentByToken(content, segmentMaxTokens); + + // 3. 转换为段落对象 + return convertList(documentSegments, segment -> { + if (StrUtil.isEmpty(segment.getText())) { + return null; + } + return new AiKnowledgeSegmentDO() + .setContent(segment.getText()) + .setContentLength(segment.getText().length()) + .setTokens(tokenCountEstimator.estimate(segment.getText())); + }); + } + /** * 校验段落是否存在 * @@ -202,6 +221,11 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService return getVectorStoreById(knowledgeService.validateKnowledgeExists(knowledgeId)); } + private static List splitContentByToken(String content, Integer segmentMaxTokens) { + TextSplitter textSplitter = buildTokenTextSplitter(segmentMaxTokens); + return textSplitter.apply(Collections.singletonList(new Document(content))); + } + private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) { return TokenTextSplitter.builder() .withChunkSize(segmentMaxTokens) From a998168b3b62713821c15b5798115bc589eb98f4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 11:42:11 +0800 Subject: [PATCH 226/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E6=96=87=E6=A1=A3=E7=9A=84=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=B8=8E=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeDocumentController.java | 13 +++++-- .../AiKnowledgeSegmentController.java | 3 +- .../AiKnowledgeDocumentUpdateReqVO.java | 8 ++--- .../knowledge/AiKnowledgeSegmentMapper.java | 6 ++++ .../knowledge/AiKnowledgeDocumentService.java | 14 ++++++-- .../AiKnowledgeDocumentServiceImpl.java | 34 +++++++++++++++++-- .../knowledge/AiKnowledgeSegmentService.java | 7 ++++ .../AiKnowledgeSegmentServiceImpl.java | 16 +++++++++ 8 files changed, 87 insertions(+), 14 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index 12e8ad1fcd..e828615ea1 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowl import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; @@ -47,14 +48,14 @@ public class AiKnowledgeDocumentController { } @PostMapping("/create") - @Operation(summary = "新建文档") + @Operation(summary = "新建文档(单个)") public CommonResult createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) { Long id = documentService.createKnowledgeDocument(reqVO); return success(id); } @PostMapping("/create-list") - @Operation(summary = "批量新建文档") + @Operation(summary = "新建文档(多个)") public CommonResult> createKnowledgeDocumentList( @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) { List ids = documentService.createKnowledgeDocumentList(reqVO); @@ -68,4 +69,12 @@ public class AiKnowledgeDocumentController { return success(true); } + @PutMapping("/update-status") + @Operation(summary = "更新文档状态") + public CommonResult updateKnowledgeDocumentStatus( + @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) { + documentService.updateKnowledgeDocumentStatus(reqVO); + return success(true); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index 6177f5411a..2b21e0d1e4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.hibernate.validator.constraints.URL; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -62,7 +63,7 @@ public class AiKnowledgeSegmentController { @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true) }) public CommonResult> splitContent( - @RequestParam("url") String url, + @RequestParam("url") @URL String url, @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) { List segments = segmentService.splitContent(url, segmentMaxTokens); return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class)); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java index 1097f40606..e6dbe5cbd7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java @@ -1,12 +1,9 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; -// TODO @芋艿:稍后优化 @Schema(description = "管理后台 - AI 知识库文档更新 Request VO") @Data public class AiKnowledgeDocumentUpdateReqVO { @@ -18,8 +15,7 @@ public class AiKnowledgeDocumentUpdateReqVO { @Schema(description = "名称", example = "Java 开发手册") private String name; - @Schema(description = "是否启用", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; + @Schema(description = "分片最大 Token 数", example = "1000") + private Integer segmentMaxTokens; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index 30b38f6c8c..8381c84300 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -31,4 +31,10 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX selectListByDocumentId(Long documentId) { + return selectList(new LambdaQueryWrapperX() + .eq(AiKnowledgeSegmentDO::getDocumentId, documentId) + .orderByDesc(AiKnowledgeSegmentDO::getId)); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 8bbee98179..4a45422d82 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; @@ -10,14 +11,14 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO import java.util.List; /** - * AI 知识库-文档 Service 接口 + * AI 知识库文档 Service 接口 * * @author xiaoxin */ public interface AiKnowledgeDocumentService { /** - * 创建文档 + * 创建文档(单个) * * @param createReqVO 文档创建 Request VO * @return 文档编号 @@ -25,7 +26,7 @@ public interface AiKnowledgeDocumentService { Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO); /** - * 批量创建文档 + * 创建文档(多个) * * @param createListReqVO 批量创建 Request VO * @return 文档编号列表 @@ -55,6 +56,13 @@ public interface AiKnowledgeDocumentService { */ void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO); + /** + * 更新文档状态 + * + * @param reqVO 更新状态信息 + */ + void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO); + /** * 校验文档是否存在 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 6ed7fdd57d..4cff8324c6 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -8,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; @@ -88,6 +90,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic .setKnowledgeId(createListReqVO.getKnowledgeId()) .setContent(content).setContentLength(content.length()) .setTokens(tokenCountEstimator.estimate(content)) + .setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens()) .setStatus(CommonStatusEnum.ENABLE.getStatus())); } knowledgeDocumentMapper.insertBatch(documentDOs); @@ -111,11 +114,38 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Override public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) { // 1. 校验文档是否存在 - validateKnowledgeDocumentExists(reqVO.getId()); + AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId()); + // 2. 更新文档 AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class); knowledgeDocumentMapper.updateById(document); - // TODO @芋艿:这里要处理状态的变更 + + // 3. 如果处于开启状态,并且最大 tokens 发生变化,则 segment 需要重新索引 + if (CommonStatusEnum.isEnable(oldDocument.getStatus()) + && reqVO.getSegmentMaxTokens() != null + && ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) { + // 删除旧的文档切片 + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId()); + // 重新创建文档切片 + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent()); + } + } + + @Override + public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) { + // 1. 校验存在 + AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId()); + + // 2. 更新状态 + knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO() + .setId(reqVO.getId()).setStatus(reqVO.getStatus())); + + // 3. 处理文档切片 + if (CommonStatusEnum.isEnable(reqVO.getStatus())) { + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent()); + } else { + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId()); + } } @Override diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index b105dcb8eb..137887af77 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -58,6 +58,13 @@ public interface AiKnowledgeSegmentService { */ void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); + /** + * 根据文档编号删除段落 + * + * @param documentId 文档编号 + */ + void deleteKnowledgeSegmentByDocumentId(Long documentId); + /** * 召回段落 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 37599b01fc..a20a18e8c3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -115,6 +115,22 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } } + @Override + public void deleteKnowledgeSegmentByDocumentId(Long documentId) { + // 1. 查询需要删除的段落 + List segments = segmentMapper.selectListByDocumentId(documentId); + if (CollUtil.isEmpty(segments)) { + return; + } + + // 2. 批量删除段落记录 + segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); + + // 3. 删除向量存储中的段落 + VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId()); + vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId)); + } + @Override public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) { // 1. 校验 From ebd93514b3cbb722dbb0a4f5b7c7490ffce0b417 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 13:35:00 +0800 Subject: [PATCH 227/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20document=20?= =?UTF-8?q?=E5=90=91=E9=87=8F=E7=9A=84=E8=BF=9B=E5=BA=A6=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeSegmentController.http | 9 ++++++++- .../AiKnowledgeSegmentController.java | 10 ++++++++++ .../AiKnowledgeSegmentProcessRespVO.java | 19 +++++++++++++++++++ .../AiKnowledgeSegmentUpdateReqVO.java | 2 +- .../knowledge/AiKnowledgeSegmentMapper.java | 15 +++++++++++++++ .../AiKnowledgeDocumentServiceImpl.java | 4 ++-- .../knowledge/AiKnowledgeSegmentService.java | 9 +++++++++ .../AiKnowledgeSegmentServiceImpl.java | 13 +++++++++---- 8 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http index 25d622d4c1..eaeee5704f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http @@ -2,4 +2,11 @@ GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} \ No newline at end of file +tenant-id: {{adminTenantId}} + +### 获取文档处理列表 +GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index 2b21e0d1e4..d683c32d6a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; import io.swagger.v3.oas.annotations.Operation; @@ -69,4 +70,13 @@ public class AiKnowledgeSegmentController { return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class)); } + @GetMapping("/get-process-list") + @Operation(summary = "获取文档处理列表") + @Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3") + public CommonResult> getKnowledgeSegmentProcessList( + @RequestParam("documentIds") List documentIds) { + List list = segmentService.getKnowledgeSegmentProcessList(documentIds); + return success(list); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java new file mode 100644 index 0000000000..a6b95265b7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库段落向量进度 Response VO") +@Data +public class AiKnowledgeSegmentProcessRespVO { + + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long documentId; + + @Schema(description = "总段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Long count; + + @Schema(description = "已向量化段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") + private Long embeddingCount; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java index 23b1461e2d..2fc7a3141f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java @@ -4,7 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO") +@Schema(description = "管理后台 - AI 更新 知识库段落 request VO") @Data public class AiKnowledgeSegmentUpdateReqVO { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index 8381c84300..6f731811fe 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -3,11 +3,15 @@ package cn.iocoder.yudao.module.ai.dal.mysql.knowledge; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.List; +import java.util.Collection; /** * AI 知识库分片 Mapper @@ -37,4 +41,15 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX selectProcessList(Collection documentIds) { + MPJLambdaWrapper wrapper = new MPJLambdaWrapperX() + .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId) + .selectCount(AiKnowledgeSegmentDO::getId, "count") + .select("COUNT(CASE WHEN vector_id > '" + AiKnowledgeSegmentDO.VECTOR_ID_EMPTY + + "' THEN 1 ELSE NULL END) AS embeddingCount") + .in(AiKnowledgeSegmentDO::getDocumentId, documentIds) + .groupBy(AiKnowledgeSegmentDO::getDocumentId); + return selectJoinList(AiKnowledgeSegmentProcessRespVO.class, wrapper); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 4cff8324c6..466b440cb7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -67,8 +67,8 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic .setStatus(CommonStatusEnum.ENABLE.getStatus()); knowledgeDocumentMapper.insert(documentDO); - // 4. 文档切片入库(同步) - knowledgeSegmentService.createKnowledgeSegmentBySplitContent(documentDO.getId(), content); + // 4. 文档切片入库(异步) + knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(documentDO.getId(), content); return documentDO.getId(); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 137887af77..a1a0b2d455 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import org.springframework.scheduling.annotation.Async; @@ -82,4 +83,12 @@ public interface AiKnowledgeSegmentService { */ List splitContent(String url, Integer segmentMaxTokens); + /** + * 获取文档处理进度(多个) + * + * @param documentIds 文档编号列表 + * @return 文档处理列表 + */ + List getKnowledgeSegmentProcessList(List documentIds); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index a20a18e8c3..0dce49f2be 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -6,10 +6,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; @@ -252,4 +249,12 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService .build(); } + @Override + public List getKnowledgeSegmentProcessList(List documentIds) { + if (CollUtil.isEmpty(documentIds)) { + return Collections.emptyList(); + } + return segmentMapper.selectProcessList(documentIds); + } + } From cb16539b663b2e510f8e67aa9f37b78378c68599 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 1 Mar 2025 17:43:46 +0800 Subject: [PATCH 228/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=96=B0=E5=A2=9E=20Redis=20Stream=20?= =?UTF-8?q?MQ=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iot/enums/rule/IotDataBridgTypeEnum.java | 2 +- .../dal/dataobject/rule/IotDataBridgeDO.java | 33 +++- .../AbstractCacheableDataBridgeExecute.java | 10 ++ .../databridge/IotDataBridgeExecute.java | 10 -- .../IotKafkaMQDataBridgeExecute.java | 1 + .../IotRabbitMQDataBridgeExecute.java | 2 +- .../IotRedisStreamMQDataBridgeExecute.java | 147 ++++++++++++++++++ 7 files changed, 189 insertions(+), 16 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java index cdec8d7979..295f35cffd 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java @@ -22,7 +22,7 @@ public enum IotDataBridgTypeEnum implements ArrayValuable { MQTT(10), DATABASE(20), - REDIS(21), + REDIS_STREAM(21), ROCKETMQ(30), RABBITMQ(31), diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index e95cb695ee..bb0623a1ff 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -193,10 +193,6 @@ public class IotDataBridgeDO extends BaseDO { */ private Boolean ssl; - /** - * 生产者组 ID - */ - private String groupId; /** * 主题 */ @@ -245,4 +241,33 @@ public class IotDataBridgeDO extends BaseDO { private String queue; } + /** + * Redis Stream MQ 配置 + */ + @Data + public static class RedisStreamMQConfig implements Config { + + /** + * Redis 服务器地址 + */ + private String host; + /** + * 端口 + */ + private Integer port; + /** + * 密码 + */ + private String password; + /** + * 数据库索引 + */ + private Integer database; + + /** + * 主题 + */ + private String topic; + } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index e8fbb0ccb3..96b1edd330 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -7,6 +7,16 @@ import lombok.extern.slf4j.Slf4j; import java.time.Duration; +// TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; +// TODO @芋艿:mq-redis +// TODO @芋艿:mq-数据库 +// TODO @芋艿:kafka +// TODO @芋艿:rocketmq +// TODO @芋艿:rabbitmq +// TODO @芋艿:mqtt +// TODO @芋艿:tcp +// TODO @芋艿:websocket + /** * 带缓存功能的数据桥梁执行器抽象类 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index 1617c3b091..a2593f3d9b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -19,14 +19,4 @@ public interface IotDataBridgeExecute { */ void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge); - // TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; - // TODO @芋艿:mq-redis - // TODO @芋艿:mq-数据库 - // TODO @芋艿:kafka - // TODO @芋艿:rocketmq - // TODO @芋艿:rabbitmq - // TODO @芋艿:mqtt - // TODO @芋艿:tcp - // TODO @芋艿:websocket - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 8c0ef2b038..ec560122a8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -35,6 +35,7 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig()); } + @SuppressWarnings("unchecked") private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) { try { // 1. 获取或创建 KafkaTemplate diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 2c257051f6..78c1343c2d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -87,7 +87,7 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe } } - // TODO @芋道源码:测试代码,后续清理 + // TODO @芋艿:测试代码,后续清理 public static void main(String[] args) { // 1. 创建一个共享的实例 IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute(); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java new file mode 100644 index 0000000000..4eed88aec6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; + +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.SingleServerConfig; +import org.redisson.spring.data.connection.RedissonConnectionFactory; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.stream.ObjectRecord; +import org.springframework.data.redis.connection.stream.StreamRecords; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * Redis Stream MQ 的 {@link IotDataBridgeExecute} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { + + @Override + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥梁类型 + if (!IotDataBridgTypeEnum.REDIS_STREAM.getType().equals(dataBridge.getType())) { + return; + } + // 1.2 执行消息发送 + executeRedisStream(message, (IotDataBridgeDO.RedisStreamMQConfig) dataBridge.getConfig()); + } + + @SuppressWarnings("unchecked") + private void executeRedisStream(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) { + try { + // 1. 获取 RedisTemplate + RedisTemplate redisTemplate = (RedisTemplate) getProducer(config); + + // 2. 创建并发送 Stream 记录 + ObjectRecord record = StreamRecords.newRecord() + .ofObject(message).withStreamKey(config.getTopic()); + String recordId = String.valueOf(redisTemplate.opsForStream().add(record)); + log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config); + } catch (Exception e) { + log.error("[executeRedisStream][消息发送失败] message: {}, config: {}", message, config, e); + } + } + + @Override + protected Object initProducer(Object config) { + IotDataBridgeDO.RedisStreamMQConfig redisConfig = (IotDataBridgeDO.RedisStreamMQConfig) config; + + // 1.1 创建 Redisson 配置 + Config redissonConfig = new Config(); + SingleServerConfig serverConfig = redissonConfig.useSingleServer() + .setAddress("redis://" + redisConfig.getHost() + ":" + redisConfig.getPort()) + .setDatabase(redisConfig.getDatabase()); + // 1.2 设置密码(如果有) + if (StrUtil.isNotBlank(redisConfig.getPassword())) { + serverConfig.setPassword(redisConfig.getPassword()); + } + + // 2.1 创建 RedissonClient + RedissonClient redisson = Redisson.create(redissonConfig); + // 2.2 创建并配置 RedisTemplate + RedisTemplate template = new RedisTemplate<>(); + // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友,可以自己去撸下。 + template.setConnectionFactory(new RedissonConnectionFactory(redisson)); + // 使用 String 序列化方式,序列化 KEY 。 + template.setKeySerializer(RedisSerializer.string()); + template.setHashKeySerializer(RedisSerializer.string()); + // 使用 JSON 序列化方式(库是 Jackson ),序列化 VALUE 。 + template.setValueSerializer(buildRedisSerializer()); + template.setHashValueSerializer(buildRedisSerializer()); + template.afterPropertiesSet();// 初始化 + return template; + } + + @Override + protected void closeProducer(Object producer) { + if (producer instanceof RedisTemplate) { + RedisConnectionFactory factory = ((RedisTemplate) producer).getConnectionFactory(); + try { + if (factory != null) { + ((RedissonConnectionFactory) factory).destroy(); + } + } catch (Exception e) { + log.error("[closeProducer][关闭 redisson 连接异常]", e); + } + } + } + + + public static RedisSerializer buildRedisSerializer() { + RedisSerializer json = RedisSerializer.json(); + // 解决 LocalDateTime 的序列化 + ObjectMapper objectMapper = (ObjectMapper) ReflectUtil.getFieldValue(json, "mapper"); + objectMapper.registerModules(new JavaTimeModule()); + return json; + } + + // TODO @芋艿:测试代码,后续清理 + public static void main(String[] args) { + // 1. 创建一个共享的实例 + IotRedisStreamMQDataBridgeExecute action = new IotRedisStreamMQDataBridgeExecute(); + + // 2. 创建共享的配置 + IotDataBridgeDO.RedisStreamMQConfig config = new IotDataBridgeDO.RedisStreamMQConfig(); + config.setHost("127.0.0.1"); + config.setPort(6379); + config.setDatabase(0); + config.setPassword("123456"); + config.setTopic("test-stream"); + + // 3. 创建共享的消息 + IotDeviceMessage message = IotDeviceMessage.builder() + .requestId("TEST-001") + .productKey("testProduct") + .deviceName("testDevice") + .deviceKey("testDeviceKey") + .type("property") + .identifier("temperature") + .data("{\"value\": 60}") + .reportTime(LocalDateTime.now()) + .tenantId(1L) + .build(); + + // 4. 执行两次测试,验证缓存 + log.info("[main][第一次执行,应该会创建新的 RedisTemplate]"); + action.executeRedisStream(message, config); + + log.info("[main][第二次执行,应该会复用缓存的 RedisTemplate]"); + action.executeRedisStream(message, config); + } + +} \ No newline at end of file From 53693529e1d06a417f7e1d8f9f68e0478cdb4fca Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 23:30:47 +0800 Subject: [PATCH 229/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E9=A6=96=E9=A1=B5=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../statistics/IotStatisticsController.java | 31 +++++++------- ...tStatisticsDeviceMessageSummaryRespVO.java | 4 +- .../statistics/vo/IotStatisticsReqVO.java | 5 +-- .../vo/IotStatisticsSummaryRespVO.java | 40 +++++-------------- .../iot/dal/mysql/device/IotDeviceMapper.java | 9 +---- .../iot/dal/tdengine/IotDeviceLogMapper.java | 8 ++-- .../iot/service/device/IotDeviceService.java | 8 ---- .../service/device/IotDeviceServiceImpl.java | 8 +--- .../device/data/IotDeviceLogService.java | 9 ++--- .../device/data/IotDeviceLogServiceImpl.java | 4 +- .../product/IotProductCategoryService.java | 3 +- .../IotProductCategoryServiceImpl.java | 5 +-- 13 files changed, 46 insertions(+), 90 deletions(-) diff --git a/pom.xml b/pom.xml index a343309726..0b8e28284d 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - + yudao-module-bpm diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index c4ef83a4ac..31cc5121ad 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -36,43 +36,42 @@ public class IotStatisticsController { @Resource private IotDeviceLogService deviceLogService; - // TODO @super:description 非必要,可以不写哈 @GetMapping("/get-summary") - @Operation(summary = "获取IOT数据统计") + @Operation(summary = "获取 IoT 数据统计") public CommonResult getIotStatisticsSummary(){ IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO(); - // 获取总数 + // 1.1 获取总数 respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null)); respVO.setProductCount(productService.getProductCount(null)); respVO.setDeviceCount(deviceService.getDeviceCount(null)); respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null)); - - // 获取今日新增数量 + // 1.2 获取今日新增数量 + // TODO @super:使用 LocalDateTimeUtils.getToday() LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart)); respVO.setProductTodayCount(productService.getProductCount(todayStart)); respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart)); respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart)); - // 获取各个品类下设备数量统计 + // 2. 获取各个品类下设备数量统计 respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap()); - // 获取设备状态数量统计 + // 3. 获取设备状态数量统计 Map deviceCountMap = deviceService.getDeviceCountMapByState(); respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L)); respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L)); respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L)); - return CommonResult.success(respVO); } - @GetMapping("/get-log-summary") - @Operation(summary = "获取IOT上下行消息数据统计") - public CommonResult getIotStatisticsDeviceMessageSummary(@Valid IotStatisticsReqVO reqVO){ - // 根据传入时间范围获取设备上下行消息数量统计 - IotStatisticsDeviceMessageSummaryRespVO iotStatisticsRespVO = new IotStatisticsDeviceMessageSummaryRespVO(); - iotStatisticsRespVO.setUpstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); - iotStatisticsRespVO.setDownstreamCounts(deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())); - return CommonResult.success(iotStatisticsRespVO); + // TODO @super:要不干掉 IotStatisticsReqVO 参数,直接使用 @RequestParam 接收,简单一些。 + @GetMapping("/get-log-summary") + @Operation(summary = "获取 IoT 设备上下行消息数据统计") + public CommonResult getIotStatisticsDeviceMessageSummary( + @Valid IotStatisticsReqVO reqVO) { + return CommonResult.success(new IotStatisticsDeviceMessageSummaryRespVO() + .setDownstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())) + .setDownstreamCounts((deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())))); } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java index 250c63e2ab..15d2abccc6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java @@ -6,14 +6,14 @@ import lombok.Data; import java.util.List; import java.util.Map; -@Schema(description = "管理后台 - Iot 上下行消息数量统计 Response VO") +@Schema(description = "管理后台 - IoT 设备上下行消息数量统计 Response VO") @Data public class IotStatisticsDeviceMessageSummaryRespVO { + @Schema(description = "每小时上行数据数量统计") private List> upstreamCounts; @Schema(description = "每小时下行数据数量统计") private List> downstreamCounts; - // TODO @super:如果只有这两个字段,使用 KeyValue 这个键值对 } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java index acffe1299f..741f77f3a4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsReqVO.java @@ -4,12 +4,11 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; -@Schema(description = "管理后台 - Iot统计 Request VO") +@Schema(description = "管理后台 - IoT 统计 Request VO") @Data public class IotStatisticsReqVO { - // TODO @supper:times 直接传递哈; - // TODO 2super:private 不要丢了 + // TODO @super:前端传递的时候,还是通过 startTime 和 endTime 传递。后端转成 Long @Schema(description = "查询起始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1658486600000") @NotNull(message = "查询起始时间不能为空") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java index 0ad1f9ff4f..1b750f380b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java @@ -5,8 +5,6 @@ import lombok.Data; import java.util.Map; -// TODO @super:Total 全部改成 Count -// TODO @super:IotStatisticsSummaryRespVO /** * 管理后台 - Iot 统计 Response VO */ @@ -14,56 +12,40 @@ import java.util.Map; @Data public class IotStatisticsSummaryRespVO { - // TODO @super:productCategory 哈 @Schema(description = "品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private long productCategoryCount; + private Long productCategoryCount; @Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private long productCount; + private Long productCount; @Schema(description = "设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private long deviceCount; + private Long deviceCount; - // TODO @super:deviceMessageCount;设备消息数量 @Schema(description = "上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - private long deviceMessageCount; + private Long deviceMessageCount; - // TODO @super:productCategory 哈 @Schema(description = "今日新增品类数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private long productCategoryTodayCount; + private Long productCategoryTodayCount; @Schema(description = "今日新增产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - private long productTodayCount; + private Long productTodayCount; @Schema(description = "今日新增设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - private long deviceTodayCount; + private Long deviceTodayCount; - // TODO @super:deviceMessageCount;今日设备消息数量 @Schema(description = "今日新增上报数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") - private long deviceMessageTodayCount; - - // TODO @super:deviceOnlineCount + private Long deviceMessageTodayCount; @Schema(description = "在线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") - private long deviceOnlineCount; - - // TODO @super:deviceOfflineCount + private Long deviceOnlineCount; @Schema(description = "离线数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "15") - private long deviceOfflineCount; - - // TODO @super:deviceInactivECount + private Long deviceOfflineCount; @Schema(description = "待激活设备数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5") - private long deviceInactiveCount; + private Long deviceInactiveCount; - // TODO @super:1)类型改成 Map,key 分类名、value 设备数量;2)deviceStatsOfCategory => productCategoryDeviceCounts @Schema(description = "按品类统计的设备数量") private Map productCategoryDeviceCounts; - // TODO @super:貌似界面里,用不到这个字段??? - - - // TODO @super:deviceUpMessageStats、deviceDownMessageStats 单独抽到 IotStatisticsDeviceMessageSummaryRespVO,然后里面属性就是 upstreamCounts、downstreamCounts - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java index 128b026f50..babbf29e7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java @@ -8,13 +8,11 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; import javax.annotation.Nullable; import java.time.LocalDateTime; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * IoT 设备 Mapper @@ -79,18 +77,15 @@ public interface IotDeviceMapper extends BaseMapperX { .geIfPresent(IotDeviceDO::getCreateTime, createTime)); } - default Long selectCountByState(@Nullable Integer state) { - return selectCount(new LambdaQueryWrapperX() - .eqIfPresent(IotDeviceDO::getState, state)); - } - /** * 查询指定产品下各状态的设备数量 * * @return 设备数量统计列表 */ + // TODO @super:通过 mybatis-plus 来写哈,然后返回 Map 貌似就行了?! List> selectDeviceCountMapByProductId(); + // TODO @super:通过 mybatis-plus 来写哈,然后返回 Map 貌似就行了?! /** * 查询各个状态下的设备数量 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java index 9efe2ec805..96741e6095 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogMapper.java @@ -63,14 +63,14 @@ public interface IotDeviceLogMapper { * 查询每个小时设备上行消息数量 */ List> selectDeviceLogUpCountByHour(@Param("deviceKey") String deviceKey, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime); + @Param("startTime") Long startTime, + @Param("endTime") Long endTime); /** * 查询每个小时设备下行消息数量 */ List> selectDeviceLogDownCountByHour(@Param("deviceKey") String deviceKey, - @Param("startTime") Long startTime, - @Param("endTime") Long endTime); + @Param("startTime") Long startTime, + @Param("endTime") Long endTime); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java index 0e50f4a27a..1dda3f333c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java @@ -196,14 +196,6 @@ public interface IotDeviceService { */ Long getDeviceCount(@Nullable LocalDateTime createTime); - - /** - * 获得所有设备列表 - * - * @return 设备列表 - */ - List getDeviceList(); - /** * 获取 MQTT 连接参数 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java index b6c0969d17..989f10a095 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java @@ -430,13 +430,7 @@ public class IotDeviceServiceImpl implements IotDeviceService { return deviceMapper.selectCountByCreateTime(createTime); } - // TODO @super:是不是 groupby 查询,更高效;不过 controller,还是要考虑 null 的情况;不过可以直接枚举 foreach 处理下 - - @Override - public List getDeviceList() { - return deviceMapper.selectList(); - } - + // TODO @super:简化 @Override public Map getDeviceCountMapByProductId() { // 查询结果转换成Map diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java index e1a15aaa16..468599e1ce 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; @@ -57,8 +56,8 @@ public interface IotDeviceLogService { * @return key: 时间戳, value: 消息数量 */ List> getDeviceLogUpCountByHour(@Nullable String deviceKey, - @Nullable Long startTime, - @Nullable Long endTime); + @Nullable Long startTime, + @Nullable Long endTime); /** * 获得每个小时设备下行消息数量统计 @@ -69,7 +68,7 @@ public interface IotDeviceLogService { * @return key: 时间戳, value: 消息数量 */ List> getDeviceLogDownCountByHour(@Nullable String deviceKey, - @Nullable Long startTime, - @Nullable Long endTime); + @Nullable Long startTime, + @Nullable Long endTime); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java index 29b57402b5..1df4d4cd44 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.data.IotDeviceLogPageReqVO; -import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO; import cn.iocoder.yudao.module.iot.dal.tdengine.IotDeviceLogMapper; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; @@ -73,13 +72,13 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { @Override public Long getDeviceLogCount(LocalDateTime createTime) { - // todo @super:1)LocalDateTimeUtil.toEpochMilli(createTime);2)直接表达式,更简洁 time != null ? createTime.toInstant(ZoneOffset.UTC).toEpochMilli() : null; return deviceLogMapper.selectCountByCreateTime(createTime != null ? LocalDateTimeUtil.toEpochMilli(createTime) : null); } // TODO @super:加一个参数,Boolean upstream:true 上行,false 下行,null 不过滤 @Override public List> getDeviceLogUpCountByHour(String deviceKey, Long startTime, Long endTime) { + // TODO @super:不能只基于数据库统计。因为有一些小时,可能出现没数据的情况,导致前端展示的图是不全的。可以参考 CrmStatisticsCustomerService 来实现 List> list = deviceLogMapper.selectDeviceLogUpCountByHour(deviceKey, startTime, endTime); return list.stream() .map(map -> { @@ -93,6 +92,7 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { .collect(Collectors.toList()); } + // TODO @super:getDeviceLogDownCountByHour 融合到 getDeviceLogUpCountByHour @Override public List> getDeviceLogDownCountByHour(String deviceKey, Long startTime, Long endTime) { List> list = deviceLogMapper.selectDeviceLogDownCountByHour(deviceKey, startTime, endTime); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java index 662d161c10..8cc640556b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryService.java @@ -93,9 +93,8 @@ public interface IotProductCategoryService { */ Long getProductCategoryCount(@Nullable LocalDateTime createTime); - // TODO @super:1)Map 虽然有点怪哈,然后 Controller 按需转换成 Map ;2)名字 getProductCategoryDeviceCountMap 方法 /** - * 获得各个品类下设备数量统计 + * 获得各个品类下设备数量统计,其中 key 是产品分类名 * * @return 品类设备统计列表 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index 5499937fde..f03186866d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -106,11 +106,9 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService // 2. 统计每个分类下的设备数量 Map categoryDeviceCountMap = new HashMap<>(); - - // 2.1 初始化所有分类的计数为0 for (IotProductCategoryDO category : categoryList) { categoryDeviceCountMap.put(category.getName(), 0); - + // TODO @super:CollectionUtils.getSumValue(),看看能不能简化下 // 2.2 找到该分类下的所有产品,累加设备数量 for (IotProductDO product : productList) { if (Objects.equals(product.getCategoryId(), category.getId())) { @@ -119,7 +117,6 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService } } } - return categoryDeviceCountMap; } From c6b58b0ebfbfdac305f8ae9d32de6156faba8131 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 23:58:16 +0800 Subject: [PATCH 230/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B0=E6=8D=AE=E6=A1=A5?= =?UTF-8?q?=E6=A2=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractCacheableDataBridgeExecute.java | 12 +++++------- .../action/databridge/IotDataBridgeExecute.java | 2 ++ .../databridge/IotKafkaMQDataBridgeExecute.java | 13 +++++++------ .../databridge/IotRabbitMQDataBridgeExecute.java | 1 + .../IotRedisStreamMQDataBridgeExecute.java | 5 ++++- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index 96b1edd330..ebd0f87764 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -7,12 +7,7 @@ import lombok.extern.slf4j.Slf4j; import java.time.Duration; -// TODO @芋艿:因为下面的,都是有状态的,所以通过 guava 缓存连接,然后通过 RemovalNotification 实现关闭。例如说,一次新建有效期是 10 分钟; -// TODO @芋艿:mq-redis -// TODO @芋艿:mq-数据库 -// TODO @芋艿:kafka -// TODO @芋艿:rocketmq -// TODO @芋艿:rabbitmq +// TODO @芋艿:数据库 // TODO @芋艿:mqtt // TODO @芋艿:tcp // TODO @芋艿:websocket @@ -25,6 +20,7 @@ import java.time.Duration; @Slf4j public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { + // TODO @huihui:AbstractCacheableDataBridgeExecute 这样,下面的 Object, Object 就有了类型;另外 IotDataBridgeDO.Config 可以替代一个 Object 哇, /** * Producer 缓存 */ @@ -43,12 +39,14 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg } }) .build(new CacheLoader() { + @Override public Object load(Object config) throws Exception { Object producer = initProducer(config); log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); return producer; } + }); /** @@ -77,4 +75,4 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg */ protected abstract void closeProducer(Object producer); -} \ No newline at end of file +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index a2593f3d9b..a10f75103c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; */ public interface IotDataBridgeExecute { + // TODO @huihui:要不搞个 getType?然后 execute0 由子类实现。这样,子类的 executeRedisStream ,其实就是 execute0 了。 + /** * 执行数据桥梁操作 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index ec560122a8..e95d4ced95 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -10,6 +10,7 @@ import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Component; +import java.time.Duration; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @@ -25,13 +26,15 @@ import java.util.concurrent.TimeoutException; @Slf4j public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { + private static final Duration SEND_TIMEOUT = Duration.ofMillis(10); + @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥梁的类型 == KAFKA + // 1. 校验数据桥梁的类型 == KAFKA if (!IotDataBridgTypeEnum.KAFKA.getType().equals(dataBridge.getType())) { return; } - // 1.2 执行 Kafka 发送消息 + // 2. 执行 Kafka 发送消息 executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig()); } @@ -43,7 +46,7 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec // 2. 发送消息并等待结果 kafkaTemplate.send(config.getTopic(), message.toString()) - .get(10, TimeUnit.SECONDS); // 添加超时等待 + .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待 log.info("[executeKafka][message({}) 发送成功]", message); } catch (TimeoutException e) { log.error("[executeKafka][message({}) config({}) 发送超时]", message, config, e); @@ -55,13 +58,12 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec @Override protected Object initProducer(Object config) { IotDataBridgeDO.KafkaMQConfig kafkaConfig = (IotDataBridgeDO.KafkaMQConfig) config; - + // 1.1 构建生产者配置 Map props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers()); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - // 1.2 如果配置了认证信息 if (kafkaConfig.getUsername() != null && kafkaConfig.getPassword() != null) { props.put("security.protocol", "SASL_PLAINTEXT"); @@ -70,7 +72,6 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"" + kafkaConfig.getUsername() + "\" password=\"" + kafkaConfig.getPassword() + "\";"); } - // 1.3 如果启用 SSL if (Boolean.TRUE.equals(kafkaConfig.getSsl())) { props.put("security.protocol", "SSL"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 78c1343c2d..5611873155 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -51,6 +51,7 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe } @Override + @SuppressWarnings("resource") protected Object initProducer(Object config) throws Exception { IotDataBridgeDO.RabbitMQConfig rabbitConfig = (IotDataBridgeDO.RabbitMQConfig) config; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java index 4eed88aec6..552fc3425b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -42,6 +42,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid } @SuppressWarnings("unchecked") + // TODO @huihui:try catch 交给父类来做,子类不处理异常 private void executeRedisStream(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) { try { // 1. 获取 RedisTemplate @@ -71,6 +72,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid serverConfig.setPassword(redisConfig.getPassword()); } + // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。 // 2.1 创建 RedissonClient RedissonClient redisson = Redisson.create(redissonConfig); // 2.2 创建并配置 RedisTemplate @@ -89,6 +91,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid @Override protected void closeProducer(Object producer) { + // TODO @huihui:try catch 交给父类来做。子类不处理异常 if (producer instanceof RedisTemplate) { RedisConnectionFactory factory = ((RedisTemplate) producer).getConnectionFactory(); try { @@ -101,7 +104,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid } } - + // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。 public static RedisSerializer buildRedisSerializer() { RedisSerializer json = RedisSerializer.json(); // 解决 LocalDateTime 的序列化 From 3c9985978b081cbdff7541b13f98d347b26d1fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sun, 2 Mar 2025 20:47:50 +0800 Subject: [PATCH 231/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=9B=B4=E6=96=B0=20MQTT=20=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=E9=85=8D=E7=BD=AE=E4=B8=BA=E6=95=B0=E7=BB=84=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E6=9E=84=20EMQX=20=E8=AE=A4=E8=AF=81=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E4=BC=98=E5=8C=96=E5=BC=82=E5=B8=B8=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=92=8C=E5=93=8D=E5=BA=94=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/tdengine/core/TaosAspect.java | 39 --------------- .../common/util/IotPluginCommonUtils.java | 14 ++++++ .../emqx/config/IotPluginEmqxProperties.java | 5 +- .../upstream/IotDeviceUpstreamServer.java | 6 +-- .../router/IotDeviceAuthVertxHandler.java | 48 ++++++++++--------- .../src/main/resources/application.yml | 3 +- .../plugin/http/IotHttpPluginApplication.java | 10 +--- .../http/config/IotHttpVertxPlugin.java | 14 ------ 8 files changed, 47 insertions(+), 92 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TaosAspect.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TaosAspect.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TaosAspect.java deleted file mode 100644 index d83f34d04c..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TaosAspect.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.iot.framework.tdengine.core; - -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.springframework.stereotype.Component; - -import java.sql.Timestamp; -import java.util.Map; - -// TODO @haohao:这个还需要的么? -/** - * TaosAspect 是一个处理 Taos 数据库返回值的切面。 - */ -@Aspect -@Component -@Slf4j -public class TaosAspect { - - @Around("execution(java.util.Map cn.iocoder.yudao.module.iot.dal.tdengine.*.*(..))") - public Object handleType(ProceedingJoinPoint joinPoint) { - Map result = null; - try { - result = (Map) joinPoint.proceed(); - result.replaceAll((key, value) -> { - if (value instanceof byte[]) { - return new String((byte[]) value); - } else if (value instanceof Timestamp) { - return ((Timestamp) value).getTime(); - } - return value; - }); - } catch (Throwable e) { - log.error("TaosAspect handleType error", e); - } - return result; - } -} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java index a632c73c70..753e62c94b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java @@ -49,4 +49,18 @@ public class IotPluginCommonUtils { .end(result); } + /** + * 将对象转换为JSON字符串后写入响应 + * + * @param routingContext 路由上下文 + * @param data 要转换为JSON的数据对象 + */ + @SuppressWarnings("deprecation") + public static void writeJson(RoutingContext routingContext, Object data) { + routingContext.response() + .setStatusCode(200) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .end(JsonUtils.toJsonString(data)); + } + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java index 72a085bd9d..4117e71820 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java @@ -36,11 +36,10 @@ public class IotPluginEmqxProperties { */ private boolean mqttSsl; - // TODO @haohao:这个是不是改成数组? /** - * 订阅的主题 + * 订阅的主题列表 */ - private String mqttTopics; + private String[] mqttTopics; /** * 认证端口 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index 5dd627671f..54479df158 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -67,6 +67,7 @@ public class IotDeviceUpstreamServer { router.route().handler(BodyHandler.create()); // 处理 Body router.post(IotDeviceAuthVertxHandler.PATH) // TODO @haohao:疑问,mqtt 的认证,需要通过 http 呀? + // 回复:MQTT 认证不必须通过 HTTP 进行,但 HTTP 认证是 EMQX 等 MQTT 服务器支持的一种灵活的认证方式 .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); @@ -165,15 +166,14 @@ public class IotDeviceUpstreamServer { * @return 订阅结果的Future */ private Future subscribeToTopics() { - String topicsStr = emqxProperties.getMqttTopics(); - if (topicsStr == null || topicsStr.trim().isEmpty()) { + String[] topics = emqxProperties.getMqttTopics(); + if (topics == null || topics.length == 0) { log.warn("[subscribeToTopics] 未配置MQTT主题,跳过订阅"); return Future.succeededFuture(); } log.info("[subscribeToTopics] 开始订阅设备上行消息主题"); - String[] topics = topicsStr.split(TOPIC_SEPARATOR); Future compositeFuture = Future.succeededFuture(); for (String topic : topics) { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java index 8eac1ffbde..350de674cd 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -10,9 +10,12 @@ import io.vertx.ext.web.RoutingContext; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import java.util.Collections; + /** * IoT Emqx 连接认证的 Vert.x Handler - * ... + * ... * * @author haohao */ @@ -27,30 +30,29 @@ public class IotDeviceAuthVertxHandler implements Handler { @Override @SuppressWarnings("unchecked") public void handle(RoutingContext routingContext) { - // TODO @haohao:try catch 兜底异常 - JsonObject json = routingContext.body().asJsonObject(); - String clientId = json.getString("clientid"); - String username = json.getString("username"); - String password = json.getString("password"); + try { + JsonObject json = routingContext.body().asJsonObject(); + String clientId = json.getString("clientid"); + String username = json.getString("username"); + String password = json.getString("password"); - IotDeviceEmqxAuthReqDTO authReqDTO = buildDeviceEmqxAuthReqDTO(clientId, username, password); + // 构建认证请求DTO + IotDeviceEmqxAuthReqDTO authReqDTO = new IotDeviceEmqxAuthReqDTO() + .setClientId(clientId) + .setUsername(username) + .setPassword(password); - CommonResult authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); - if (authResult.getCode() != 0 || !authResult.getData()) { - denyAccess(routingContext); - return; + // 调用认证API + CommonResult authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); + if (authResult.getCode() != 0 || !authResult.getData()) { + IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "deny")); + return; + } + + IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "allow")); + } catch (Exception e) { + log.error("[handle][EMQX认证异常]", e); + IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "deny")); } - // TODO @haohao:貌似可以考虑封装一个 writeJson ,里面有个参数是 data,然后里面去 JsonUtils.toJsonString(data) - IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"allow\"}"); } - - // TODO @haohao:下面两个简单方法,貌似可以考虑不抽小方法哈。 - private void denyAccess(RoutingContext routingContext) { - IotPluginCommonUtils.writeJson(routingContext, "{\"result\": \"deny\"}"); - } - - private IotDeviceEmqxAuthReqDTO buildDeviceEmqxAuthReqDTO(String clientId, String username, String password) { - return new IotDeviceEmqxAuthReqDTO().setClientId(clientId).setUsername(username).setPassword(password); - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml index 9343d3614e..c00621c82a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/resources/application.yml @@ -15,5 +15,6 @@ yudao: mqtt-ssl: false mqtt-username: yudao mqtt-password: 123456 - mqtt-topics: "/sys/#" + mqtt-topics: + - "/sys/#" auth-port: 8101 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/IotHttpPluginApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/IotHttpPluginApplication.java index 0aa08505aa..a88b34eb31 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/IotHttpPluginApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/IotHttpPluginApplication.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.ConfigurableApplicationContext; /** * 独立运行入口 @@ -16,14 +15,7 @@ public class IotHttpPluginApplication { public static void main(String[] args) { SpringApplication application = new SpringApplication(IotHttpPluginApplication.class); application.setWebApplicationType(WebApplicationType.NONE); - ConfigurableApplicationContext context = application.run(args); - - // 手动获取 VertxService 并启动 - // TODO @haohao:可以放在 bean 的 init 里么?回复:会和插件模式冲突 @芋艿,测试下 - // TODO @haohao:貌似去掉,没有问题额。。。 -// IotDeviceUpstreamServer vertxService = context.getBean(IotDeviceUpstreamServer.class); -// vertxService.start(); - + application.run(args); log.info("[main][独立模式启动完成]"); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java index ac9a933401..674980d005 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java @@ -2,11 +2,9 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.hutool.core.lang.Assert; import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPlugin; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -29,11 +27,6 @@ public class IotHttpVertxPlugin extends SpringPlugin { ApplicationContext pluginContext = getApplicationContext(); Assert.notNull(pluginContext, "pluginContext 不能为空"); - // 2. 启动 Vert.x - // TODO @haohao:貌似去掉,没有问题额。。。 -// IotDeviceUpstreamServer vertxService = pluginContext.getBean(IotDeviceUpstreamServer.class); -// vertxService.start(); - log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动成功...]"); } catch (Exception e) { log.error("[HttpVertxPlugin][HttpVertxPlugin 插件开启动异常...]", e); @@ -44,13 +37,6 @@ public class IotHttpVertxPlugin extends SpringPlugin { public void stop() { log.info("[HttpVertxPlugin][HttpVertxPlugin 插件停止开始...]"); try { - // 停止服务器 -// ApplicationContext pluginContext = getApplicationContext(); -// if (pluginContext != null) { -// IotDeviceUpstreamServer vertxService = pluginContext.getBean(IotDeviceUpstreamServer.class); -// vertxService.stop(); -// } - log.info("[HttpVertxPlugin][HttpVertxPlugin 插件停止成功...]"); } catch (Exception e) { log.error("[HttpVertxPlugin][HttpVertxPlugin 插件停止异常...]", e); From 5f5e77a3923bc39b3ef160195e00df5f3baee1aa Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Mar 2025 20:54:02 +0800 Subject: [PATCH 232/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20document=20?= =?UTF-8?q?=E5=90=91=E9=87=8F=E7=9A=84=E8=BF=9B=E5=BA=A6=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledge/AiKnowledgeController.http | 6 +- .../AiKnowledgeDocumentController.http | 2 +- .../AiKnowledgeSegmentController.http | 9 +- .../AiKnowledgeSegmentController.java | 37 +++++- .../AiKnowledgeDocumentUpdateStatusReqVO.java | 22 ++++ .../AiKnowledgeSegmentSearchReqVO.java | 16 ++- .../AiKnowledgeSegmentSearchRespVO.java | 16 +++ .../dataobject/chat/AiChatConversationDO.java | 1 + .../knowledge/AiKnowledgeDocumentMapper.java | 9 ++ .../knowledge/AiKnowledgeSegmentMapper.java | 9 +- .../chat/AiChatMessageServiceImpl.java | 5 +- .../knowledge/AiKnowledgeDocumentService.java | 31 ++++- .../AiKnowledgeDocumentServiceImpl.java | 19 ++- .../knowledge/AiKnowledgeSegmentService.java | 13 +- .../AiKnowledgeSegmentServiceImpl.java | 43 +++++-- .../bo/AiKnowledgeSegmentSearchReqBO.java | 39 ++++++ .../bo/AiKnowledgeSegmentSearchRespBO.java | 45 +++++++ .../ai/service/model/AiApiKeyServiceImpl.java | 3 +- .../ai/core/factory/AiModelFactory.java | 3 +- .../ai/core/factory/AiModelFactoryImpl.java | 118 ++++++++++++++---- 20 files changed, 385 insertions(+), 61 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http index 235e880343..a0f127865a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http @@ -9,7 +9,8 @@ tenant-id: {{adminTenantId}} "description": "测试描述", "embeddingModelId": 30, "topK": 3, - "similarityThreshold": 0.5 + "similarityThreshold": 0.5, + "status": 0 } ### 更新知识库 @@ -24,7 +25,8 @@ tenant-id: {{adminTenantId}} "description": "测试描述", "embeddingModelId": 30, "topK": 5, - "similarityThreshold": 0.6 + "similarityThreshold": 0.6, + "status": 0 } ### 获取知识库分页 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http index 22c1d91115..1c858ed3eb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http @@ -5,7 +5,7 @@ Authorization: Bearer {{token}} tenant-id: {{adminTenantId}} { - "knowledgeId": 1, + "knowledgeId": 2, "name": "测试文档", "url": "https://static.iocoder.cn/README.md", "segmentMaxTokens": 800 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http index eaeee5704f..09018da3dc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http @@ -4,9 +4,14 @@ Content-Type: application/json Authorization: Bearer {{token}} tenant-id: {{adminTenantId}} +### 搜索段落内容 +GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1 +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + ### 获取文档处理列表 GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3 Content-Type: application/json Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - +tenant-id: {{adminTenantId}} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index d683c32d6a..06b61708f0 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -1,15 +1,17 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -20,9 +22,12 @@ import org.hibernate.validator.constraints.URL; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.Collections; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; // TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库段落") @@ -34,6 +39,9 @@ public class AiKnowledgeSegmentController { @Resource private AiKnowledgeSegmentService segmentService; + @Resource + private AiKnowledgeDocumentService documentService; + @GetMapping("/page") @Operation(summary = "获取段落分页") public CommonResult> getKnowledgeSegmentPage( @@ -79,4 +87,23 @@ public class AiKnowledgeSegmentController { return success(list); } + @GetMapping("/search") + @Operation(summary = "搜索段落内容") + public CommonResult> searchKnowledgeSegment( + @Valid AiKnowledgeSegmentSearchReqVO reqVO) { + // 1. 搜索段落 + List segments = segmentService + .searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class)); + if (CollUtil.isEmpty(segments)) { + return success(Collections.emptyList()); + } + + // 2. 拼接 VO + Map documentMap = documentService.getKnowledgeDocumentMap(convertSet( + segments, AiKnowledgeSegmentSearchRespBO::getDocumentId)); + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class, + segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(), + document -> segment.setDocumentName(document.getName())))); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java new file mode 100644 index 0000000000..93d393ab4a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库文档更新状态 Request VO") +@Data +public class AiKnowledgeDocumentUpdateStatusReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583") + @NotNull(message = "编号不能为空") + private Long id; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @NotNull(message = "状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java index 75349df628..3b3cd984b9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java @@ -3,15 +3,25 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; -@Schema(description = "管理后台 - AI 知识库段落召回 Request VO") +@Schema(description = "管理后台 - AI 知识库段落搜索 Request VO") @Data public class AiKnowledgeSegmentSearchReqVO { - @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "知识库编号不能为空") private Long knowledgeId; - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线") + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "如何使用这个产品") + @NotEmpty(message = "内容不能为空") private String content; + @Schema(description = "最大返回数量", example = "5") + private Integer topK; + + @Schema(description = "相似度阈值", example = "0.7") + private Double similarityThreshold; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java new file mode 100644 index 0000000000..50bbc5c867 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI 知识库段落搜索 Response VO") +@Data +public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO { + + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册") + private String documentName; + + @Schema(description = "相似度分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95") + private Double score; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java index cc8ae7d42a..b5e9d06541 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java @@ -65,6 +65,7 @@ public class AiChatConversationDO extends BaseDO { */ private Long roleId; + // TODO @芋艿:可优化,绑定多个知识库。前提,spring ai 支持 RerankModel 的封装 /** * 知识库编号 *

diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java index 8e6bb4d2b7..eeacca6345 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; + /** * AI 知识库文档 Mapper * @@ -22,4 +25,10 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX ids) { + update( new LambdaUpdateWrapper() + .setSql(" retrieval_count = retrieval_count + 1") + .in(AiKnowledgeDocumentDO::getId, ids)); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index 6f731811fe..fab5572ceb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -7,11 +7,12 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.apache.ibatis.annotations.Mapper; -import java.util.List; import java.util.Collection; +import java.util.List; /** * AI 知识库分片 Mapper @@ -52,4 +53,10 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX ids) { + update( new LambdaUpdateWrapper() + .setSql(" retrieval_count = retrieval_count + 1") + .in(AiKnowledgeSegmentDO::getId, ids)); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 8c4a08e1d0..1c8927e07a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -12,7 +12,6 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; @@ -133,7 +132,6 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { Flux streamResponse = chatModel.stream(prompt); // 3.4 流式返回 - // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题 StringBuffer contentBuffer = new StringBuffer(); return streamResponse.map(chunk -> { String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; @@ -159,7 +157,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { if (Objects.isNull(knowledgeId)) { return Collections.emptyList(); } - return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content)); +// return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content)); + return null; } private Prompt buildPrompt(AiChatConversationDO conversation, List messages,List segmentList, diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 4a45422d82..32720ed6a5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -1,14 +1,18 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * AI 知识库文档 Service 接口 @@ -63,6 +67,13 @@ public interface AiKnowledgeDocumentService { */ void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO); + /** + * 更新文档检索次数(增加 +1) + * + * @param ids 文档编号列表 + */ + void updateKnowledgeDocumentRetrievalCountIncr(Collection ids); + /** * 校验文档是否存在 * @@ -79,4 +90,22 @@ public interface AiKnowledgeDocumentService { */ String readUrl(String url); + /** + * 获取文档列表 + * + * @param ids 文档编号列表 + * @return 文档列表 + */ + List getKnowledgeDocumentList(Collection ids); + + /** + * 获取文档 Map + * + * @param ids 文档编号列表 + * @return 文档 Map + */ + default Map getKnowledgeDocumentMap(Collection ids) { + return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 466b440cb7..a04bf80221 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -7,10 +7,10 @@ import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; @@ -25,6 +25,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -148,6 +149,14 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic } } + @Override + public void updateKnowledgeDocumentRetrievalCountIncr(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + knowledgeDocumentMapper.updateRetrievalCountIncr(ids); + } + @Override public AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) { AiKnowledgeDocumentDO knowledgeDocument = knowledgeDocumentMapper.selectById(id); @@ -182,4 +191,12 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return document.getText(); } + @Override + public List getKnowledgeDocumentList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return new ArrayList<>(); + } + return knowledgeDocumentMapper.selectByIds(ids); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index a1a0b2d455..ce79c8dd00 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -2,11 +2,12 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; import org.springframework.scheduling.annotation.Async; import java.util.List; @@ -67,12 +68,12 @@ public interface AiKnowledgeSegmentService { void deleteKnowledgeSegmentByDocumentId(Long documentId); /** - * 召回段落 + * 搜索知识库段落,并返回结果 * - * @param reqVO 召回请求信息 - * @return 召回的段落 + * @param reqBO 搜索请求信息 + * @return 搜索结果段落列表 */ - List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO); + List searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO); /** * 根据 URL 内容,切片创建多个段落 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 0dce49f2be..eefad0bc8b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -12,6 +13,8 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; @@ -171,25 +174,45 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } @Override - public List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) { + public List searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO) { // 1. 校验 - AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId()); + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId()); - // 2. 获取向量存储实例 + // 2.1 向量检索 VectorStore vectorStore = apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId()); - - // 3.1 向量检索 List documents = vectorStore.similaritySearch(SearchRequest.builder() - .query(reqVO.getContent()) - .topK(knowledge.getTopK()).similarityThreshold(knowledge.getSimilarityThreshold()) + .query(reqBO.getContent()) + .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK())) + .similarityThreshold(ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold())) .filterExpression(new FilterExpressionBuilder() - .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()) + .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId()).build()) .build()); if (CollUtil.isEmpty(documents)) { return ListUtil.empty(); } - // 3.2 段落召回 - return segmentMapper.selectListByVectorIds(convertList(documents, Document::getId)); + // 2.2 段落召回 + List segments = segmentMapper + .selectListByVectorIds(convertList(documents, Document::getId)); + if (CollUtil.isEmpty(segments)) { + return ListUtil.empty(); + } + + // 3. 增加召回次数 + segmentMapper.updateRetrievalCountIncrByIds(convertList(segments, AiKnowledgeSegmentDO::getId)); + + // 4. 构建结果 + List result = convertList(segments, segment -> { + Document document = CollUtil.findOne(documents, // 找到对应的文档 + doc -> Objects.equals(doc.getId(), segment.getVectorId())); + if (document == null) { + return null; + } + return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class) + .setScore(document.getScore()); + }); + result.sort((o1, o2) + -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序 + return result; } @Override diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java new file mode 100644 index 0000000000..9ff63b6460 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.ai.service.knowledge.bo; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +import jakarta.validation.constraints.NotEmpty; + +/** + * AI 知识库段落搜索 Request BO + * + * @author 芋道源码 + */ +@Data +public class AiKnowledgeSegmentSearchReqBO { + + /** + * 知识库编号 + */ + @NotNull(message = "知识库编号不能为空") + private Long knowledgeId; + + /** + * 内容 + */ + @NotEmpty(message = "内容不能为空") + private String content; + + /** + * 最大返回数量 + */ + private Integer topK; + + /** + * 相似度阈值 + */ + private Double similarityThreshold; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java new file mode 100644 index 0000000000..72eb84624a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.ai.service.knowledge.bo; + +import lombok.Data; + +/** + * AI 知识库段落搜索 Response BO + * + * @author 芋道源码 + */ +@Data +public class AiKnowledgeSegmentSearchRespBO { + + /** + * 段落编号 + */ + private Long id; + /** + * 文档编号 + */ + private Long documentId; + /** + * 知识库编号 + */ + private Long knowledgeId; + + /** + * 内容 + */ + private String content; + /** + * 内容长度 + */ + private Integer contentLength; + + /** + * Token 数量 + */ + private Integer tokens; + + /** + * 相似度分数 + */ + private Double score; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index 1ef7640f29..38d6c95495 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -16,6 +16,7 @@ import jakarta.annotation.Resource; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; +import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -154,7 +155,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { apiKey.getUrl(), chatModel.getModel()); // 创建或获取 VectorStore 对象 - return modelFactory.getOrCreateVectorStore(embeddingModel); + return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java index d4275d5e5d..3a8bf5e04d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java @@ -99,9 +99,10 @@ public interface AiModelFactory { *

* 如果不存在,则进行创建 * + * @param type 向量存储类型 * @param embeddingModel 向量模型 * @return VectorStore 对象 */ - VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel); + VectorStore getOrCreateVectorStore(Class type, EmbeddingModel embeddingModel); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 779bd8e2fb..1f9b1f21a2 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -1,9 +1,11 @@ package cn.iocoder.yudao.framework.ai.core.factory; +import cn.hutool.core.io.FileUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Singleton; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.ai.config.YudaoAiAutoConfiguration; @@ -24,6 +26,7 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; import com.azure.ai.openai.OpenAIClientBuilder; +import lombok.SneakyThrows; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties; @@ -60,7 +63,11 @@ import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; import org.springframework.web.client.RestClient; +import java.io.File; +import java.time.Duration; import java.util.List; +import java.util.Timer; +import java.util.TimerTask; /** * AI Model 模型工厂的实现类 @@ -73,7 +80,7 @@ public class AiModelFactoryImpl implements AiModelFactory { public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) { String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url); return Singleton.get(cacheKey, (Func0) () -> { - //noinspection EnhancedSwitchMigration + // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: return buildTongYiChatModel(apiKey); @@ -105,7 +112,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @Override public ChatModel getDefaultChatModel(AiPlatformEnum platform) { - //noinspection EnhancedSwitchMigration + // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: return SpringUtil.getBean(DashScopeChatModel.class); @@ -136,7 +143,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @Override public ImageModel getDefaultImageModel(AiPlatformEnum platform) { - //noinspection EnhancedSwitchMigration + // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: return SpringUtil.getBean(DashScopeImageModel.class); @@ -155,7 +162,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @Override public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) { - //noinspection EnhancedSwitchMigration + // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: return buildTongYiImagesModel(apiKey); @@ -174,9 +181,11 @@ public class AiModelFactoryImpl implements AiModelFactory { @Override public MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url) { - String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey, url); + String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey, + url); return Singleton.get(cacheKey, (Func0) () -> { - YudaoAiProperties.MidjourneyProperties properties = SpringUtil.getBean(YudaoAiProperties.class).getMidjourney(); + YudaoAiProperties.MidjourneyProperties properties = SpringUtil.getBean(YudaoAiProperties.class) + .getMidjourney(); return new MidjourneyApi(url, apiKey, properties.getNotifyUrl()); }); } @@ -204,25 +213,31 @@ public class AiModelFactoryImpl implements AiModelFactory { } @Override - public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel) { -// String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url); - String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel); + public VectorStore getOrCreateVectorStore(Class type, EmbeddingModel embeddingModel) { + // String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, + // url); + String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); return Singleton.get(cacheKey, (Func0) () -> { + if (type == SimpleVectorStore.class) { + return buildSimpleVectorStore(embeddingModel); + } + throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); // TODO @芋艿:先临时使用 store - return SimpleVectorStore.builder(embeddingModel).build(); // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的 -// String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey); -// var config = RedisVectorStore.RedisVectorStoreConfig.builder() -// .withIndexName(cacheKey) -// .withPrefix(prefix) -// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC)) -// .build(); -// RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); -// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, -// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), -// true); -// redisVectorStore.afterPropertiesSet(); -// return redisVectorStore; + // String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey); + // var config = RedisVectorStore.RedisVectorStoreConfig.builder() + // .withIndexName(cacheKey) + // .withPrefix(prefix) + // .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", + // Schema.FieldType.NUMERIC)) + // .build(); + // RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); + // RedisVectorStore redisVectorStore = new RedisVectorStore(config, + // embeddingModel, + // new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), + // true); + // redisVectorStore.afterPropertiesSet(); + // return redisVectorStore; }); } @@ -307,7 +322,7 @@ public class AiModelFactoryImpl implements AiModelFactory { */ private ChatModel buildSiliconFlowChatModel(String apiKey) { YudaoAiProperties.SiliconFlowProperties properties = new YudaoAiProperties.SiliconFlowProperties() - .setApiKey(apiKey); + .setApiKey(apiKey); return new YudaoAiAutoConfiguration().buildSiliconFlowChatClient(properties); } @@ -397,7 +412,8 @@ public class AiModelFactoryImpl implements AiModelFactory { */ private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { DashScopeApi dashScopeApi = new DashScopeApi(apiKey); - DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); + DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model) + .build(); return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); } @@ -407,4 +423,58 @@ public class AiModelFactoryImpl implements AiModelFactory { return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).defaultOptions(ollamaOptions).build(); } + // ========== 各种创建 VectorStore 的方法 ========== + + /** + * 注意:仅适合本地测试使用,生产建议还是使用 Qdrant、Milvus 等 + */ + @SneakyThrows + @SuppressWarnings("ResultOfMethodCallIgnored") + private SimpleVectorStore buildSimpleVectorStore(EmbeddingModel embeddingModel) { + SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build(); + // 启动加载 + File file = new File(StrUtil.format("{}/vector_store/simple_{}.json", + FileUtil.getUserHomePath(), embeddingModel.getClass().getSimpleName())); + if (!file.exists()) { + FileUtil.mkParentDirs(file); + file.createNewFile(); + } else if (file.length() > 0) { + vectorStore.load(file); + } + // 定时持久化,每分钟一次 + Timer timer = new Timer("SimpleVectorStoreTimer-" + file.getAbsolutePath()); + timer.scheduleAtFixedRate(new TimerTask() { + + @Override + public void run() { + vectorStore.save(file); + } + + }, Duration.ofMinutes(1).toMillis(), Duration.ofMinutes(1).toMillis()); + // 关闭时,进行持久化 + RuntimeUtil.addShutdownHook(() -> vectorStore.save(file)); + return vectorStore; + } + + /** + * 创建向量存储文件 + * + * @param embeddingModel 嵌入模型 + * @return 向量存储文件 + */ + private File createVectorStoreFile(EmbeddingModel embeddingModel) { + // 获取简单类名 + String simpleClassName = embeddingModel.getClass().getSimpleName(); + // 获取用户主目录 + String userHome = FileUtil.getUserHomePath(); + // 创建vector_store目录 + File vectorStoreDir = new File(userHome, "vector_store"); + if (!vectorStoreDir.exists()) { + vectorStoreDir.mkdirs(); + } + + // 创建文件 + return new File(vectorStoreDir, "simple_" + simpleClassName + ".json"); + } + } From 3f460dc620b306f995768354cdc6be227072b947 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Mar 2025 21:52:31 +0800 Subject: [PATCH 233/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E5=BA=93=E5=88=86=E6=AE=B5=E7=9A=84=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 1 + .../knowledge/AiKnowledgeController.java | 5 ++ .../AiKnowledgeDocumentController.java | 16 ++++- .../AiKnowledgeSegmentController.java | 27 ++++++++- .../segment/AiKnowledgeSegmentPageReqVO.java | 11 ++-- .../AiKnowledgeSegmentUpdateReqVO.java | 17 ------ .../knowledge/AiKnowledgeSegmentMapper.java | 2 +- .../knowledge/AiKnowledgeDocumentService.java | 7 +++ .../AiKnowledgeDocumentServiceImpl.java | 13 +++++ .../knowledge/AiKnowledgeSegmentService.java | 20 ++++++- .../AiKnowledgeSegmentServiceImpl.java | 58 ++++++++++++++----- 11 files changed, 136 insertions(+), 41 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 5ac6d2ae9e..2b236e6f6a 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -60,5 +60,6 @@ public interface ErrorCodeConstants { ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_022_008_102, "文档加载失败!"); ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_202, "段落不存在!"); + ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_022_008_203, "内容 Token 数为 {},超过最大限制 {}"); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index df49395750..f3dc6f022e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -30,6 +31,7 @@ public class AiKnowledgeController { @GetMapping("/page") @Operation(summary = "获取知识库分页") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { PageResult pageResult = knowledgeService.getKnowledgePage(pageReqVO); @@ -39,6 +41,7 @@ public class AiKnowledgeController { @GetMapping("/get") @Operation(summary = "获得知识库") @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult getKnowledge(@RequestParam("id") Long id) { AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id); return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class)); @@ -46,12 +49,14 @@ public class AiKnowledgeController { @PostMapping("/create") @Operation(summary = "创建知识库") + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") public CommonResult createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) { return success(knowledgeService.createKnowledge(createReqVO)); } @PutMapping("/update") @Operation(summary = "更新知识库") + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") public CommonResult updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) { knowledgeService.updateKnowledge(updateReqVO); return success(true); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index e828615ea1..cca856fe17 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -22,7 +23,6 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -// TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库文档") @RestController @RequestMapping("/ai/knowledge/document") @@ -34,6 +34,7 @@ public class AiKnowledgeDocumentController { @GetMapping("/page") @Operation(summary = "获取文档分页") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> getKnowledgeDocumentPage( @Valid AiKnowledgeDocumentPageReqVO pageReqVO) { PageResult pageResult = documentService.getKnowledgeDocumentPage(pageReqVO); @@ -42,6 +43,7 @@ public class AiKnowledgeDocumentController { @GetMapping("/get") @Operation(summary = "获取文档详情") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult getKnowledgeDocument(@RequestParam("id") Long id) { AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id); return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class)); @@ -49,6 +51,7 @@ public class AiKnowledgeDocumentController { @PostMapping("/create") @Operation(summary = "新建文档(单个)") + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") public CommonResult createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) { Long id = documentService.createKnowledgeDocument(reqVO); return success(id); @@ -56,6 +59,7 @@ public class AiKnowledgeDocumentController { @PostMapping("/create-list") @Operation(summary = "新建文档(多个)") + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") public CommonResult> createKnowledgeDocumentList( @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) { List ids = documentService.createKnowledgeDocumentList(reqVO); @@ -64,6 +68,7 @@ public class AiKnowledgeDocumentController { @PutMapping("/update") @Operation(summary = "更新文档") + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") public CommonResult updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) { documentService.updateKnowledgeDocument(reqVO); return success(true); @@ -71,10 +76,19 @@ public class AiKnowledgeDocumentController { @PutMapping("/update-status") @Operation(summary = "更新文档状态") + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") public CommonResult updateKnowledgeDocumentStatus( @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) { documentService.updateKnowledgeDocumentStatus(reqVO); return success(true); } + @DeleteMapping("/delete") + @Operation(summary = "删除文档") + @PreAuthorize("@ss.hasPermission('ai:knowledge:delete')") + public CommonResult deleteKnowledgeDocument(@RequestParam("id") Long id) { + documentService.deleteKnowledgeDocument(id); + return success(true); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java index 06b61708f0..34f324491b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java @@ -19,6 +19,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.hibernate.validator.constraints.URL; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -29,7 +30,6 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -// TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库段落") @RestController @RequestMapping("/ai/knowledge/segment") @@ -38,27 +38,45 @@ public class AiKnowledgeSegmentController { @Resource private AiKnowledgeSegmentService segmentService; - @Resource private AiKnowledgeDocumentService documentService; + @GetMapping("/get") + @Operation(summary = "获取段落详情") + @Parameter(name = "id", description = "段落编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") + public CommonResult getKnowledgeSegment(@RequestParam("id") Long id) { + AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id); + return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class)); + } + @GetMapping("/page") @Operation(summary = "获取段落分页") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> getKnowledgeSegmentPage( @Valid AiKnowledgeSegmentPageReqVO pageReqVO) { PageResult pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class)); } + @PostMapping("/create") + @Operation(summary = "创建段落") + @PreAuthorize("@ss.hasPermission('ai:knowledge:create')") + public CommonResult createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) { + return success(segmentService.createKnowledgeSegment(createReqVO)); + } + @PutMapping("/update") @Operation(summary = "更新段落内容") - public CommonResult updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) { + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") + public CommonResult updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) { segmentService.updateKnowledgeSegment(reqVO); return success(true); } @PutMapping("/update-status") @Operation(summary = "启禁用段落内容") + @PreAuthorize("@ss.hasPermission('ai:knowledge:update')") public CommonResult updateKnowledgeSegmentStatus( @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) { segmentService.updateKnowledgeSegmentStatus(reqVO); @@ -71,6 +89,7 @@ public class AiKnowledgeSegmentController { @Parameter(name = "url", description = "文档 URL", required = true), @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true) }) + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> splitContent( @RequestParam("url") @URL String url, @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) { @@ -81,6 +100,7 @@ public class AiKnowledgeSegmentController { @GetMapping("/get-process-list") @Operation(summary = "获取文档处理列表") @Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> getKnowledgeSegmentProcessList( @RequestParam("documentIds") List documentIds) { List list = segmentService.getKnowledgeSegmentProcessList(documentIds); @@ -89,6 +109,7 @@ public class AiKnowledgeSegmentController { @GetMapping("/search") @Operation(summary = "搜索段落内容") + @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> searchKnowledgeSegment( @Valid AiKnowledgeSegmentSearchReqVO reqVO) { // 1. 搜索段落 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java index 8be3db501b..f53d5be076 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -8,13 +10,14 @@ import lombok.Data; @Data public class AiKnowledgeSegmentPageReqVO extends PageParam { - @Schema(description = "分段状态", example = "1") - private Integer status; - @Schema(description = "文档编号", example = "1") private Integer documentId; @Schema(description = "分段内容关键字", example = "Java 开发") - private String keyword; + private String content; + + @Schema(description = "分段状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java deleted file mode 100644 index 2fc7a3141f..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - - -@Schema(description = "管理后台 - AI 更新 知识库段落 request VO") -@Data -public class AiKnowledgeSegmentUpdateReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") - private Long id; - - @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") - private String content; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index fab5572ceb..a2a4e15b6d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -25,8 +25,8 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX selectPage(AiKnowledgeSegmentPageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId()) + .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent()) .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus()) - .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword()) .orderByDesc(AiKnowledgeSegmentDO::getId)); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 32720ed6a5..8ff137b331 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -74,6 +74,13 @@ public interface AiKnowledgeDocumentService { */ void updateKnowledgeDocumentRetrievalCountIncr(Collection ids); + /** + * 删除文档 + * + * @param id 文档编号 + */ + void deleteKnowledgeDocument(Long id); + /** * 校验文档是否存在 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index a04bf80221..b1513a9bd4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -149,6 +149,19 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic } } + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteKnowledgeDocument(Long id) { + // 1. 校验存在 + validateKnowledgeDocumentExists(id); + + // 2. 删除 + knowledgeDocumentMapper.deleteById(id); + + // 3. 删除对应的段落 + knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(id); + } + @Override public void updateKnowledgeDocumentRetrievalCountIncr(Collection ids) { if (CollUtil.isEmpty(ids)) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index ce79c8dd00..15ab941fe8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; @@ -19,6 +19,14 @@ import java.util.List; */ public interface AiKnowledgeSegmentService { + /** + * 获取知识库段落详情 + * + * @param id 段落编号 + * @return 段落详情 + */ + AiKnowledgeSegmentDO getKnowledgeSegment(Long id); + /** * 获取段落分页 * @@ -46,12 +54,20 @@ public interface AiKnowledgeSegmentService { createKnowledgeSegmentBySplitContent(documentId, content); } + /** + * 创建知识库段落 + * + * @param createReqVO 创建信息 + * @return 段落编号 + */ + Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO); + /** * 更新段落的内容 * * @param reqVO 更新内容 */ - void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO); + void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO); /** * 更新段落的状态 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index eefad0bc8b..adf1616cb0 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -34,6 +34,7 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG; /** * AI 知识库分片 Service 实现类 @@ -98,20 +99,20 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } @Override - public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) { + public void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO) { // 1. 校验 - AiKnowledgeSegmentDO segment = validateKnowledgeSegmentExists(reqVO.getId()); + AiKnowledgeSegmentDO oldSegment = validateKnowledgeSegmentExists(reqVO.getId()); // 2. 删除向量 - VectorStore vectorStore = getVectorStoreById(segment.getKnowledgeId()); - deleteVectorStore(vectorStore, segment); + VectorStore vectorStore = getVectorStoreById(oldSegment.getKnowledgeId()); + deleteVectorStore(vectorStore, oldSegment); // 3.1 更新切片 - AiKnowledgeSegmentDO segmentDO = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); - segmentMapper.updateById(segmentDO); + AiKnowledgeSegmentDO newSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); + segmentMapper.updateById(newSegment); // 3.2 重新向量化,必须开启状态 - if (CommonStatusEnum.isEnable(segmentDO.getStatus())) { - writeVectorStore(vectorStore, segmentDO, new Document(segmentDO.getContent())); + if (CommonStatusEnum.isEnable(oldSegment.getStatus())) { + writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent())); } } @@ -143,7 +144,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService segmentMapper.updateById(new AiKnowledgeSegmentDO().setId(reqVO.getId()).setStatus(reqVO.getStatus())); // 4. 更新向量 - if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + if (CommonStatusEnum.isEnable(reqVO.getStatus())) { writeVectorStore(vectorStore, segment, new Document(segment.getContent())); } else { deleteVectorStore(vectorStore, segment); @@ -183,7 +184,8 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService List documents = vectorStore.similaritySearch(SearchRequest.builder() .query(reqBO.getContent()) .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK())) - .similarityThreshold(ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold())) + .similarityThreshold( + ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold())) .filterExpression(new FilterExpressionBuilder() .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId()).build()) .build()); @@ -202,7 +204,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService // 4. 构建结果 List result = convertList(segments, segment -> { - Document document = CollUtil.findOne(documents, // 找到对应的文档 + Document document = CollUtil.findOne(documents, // 找到对应的文档 doc -> Objects.equals(doc.getId(), segment.getVectorId())); if (document == null) { return null; @@ -210,8 +212,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService return BeanUtils.toBean(segment, AiKnowledgeSegmentSearchRespBO.class) .setScore(document.getScore()); }); - result.sort((o1, o2) - -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序 + result.sort((o1, o2) -> Double.compare(o2.getScore(), o1.getScore())); // 按照分数降序排序 return result; } @@ -280,4 +281,35 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService return segmentMapper.selectProcessList(documentIds); } + @Override + public Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO) { + // 1.1 校验文档是否存在 + AiKnowledgeDocumentDO document = knowledgeDocumentService + .validateKnowledgeDocumentExists(createReqVO.getDocumentId()); + // 1.2 获取知识库信息 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(document.getKnowledgeId()); + // 1.3 校验 token 熟练 + Integer tokens = tokenCountEstimator.estimate(createReqVO.getContent()); + if (tokens > document.getSegmentMaxTokens()) { + throw exception(KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG, tokens, document.getSegmentMaxTokens()); + } + + // 2. 保存段落 + AiKnowledgeSegmentDO segment = BeanUtils.toBean(createReqVO, AiKnowledgeSegmentDO.class) + .setKnowledgeId(knowledge.getId()).setDocumentId(document.getId()) + .setContentLength(createReqVO.getContent().length()).setTokens(tokens) + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY) + .setRetrievalCount(0).setStatus(CommonStatusEnum.ENABLE.getStatus()); + segmentMapper.insert(segment); + + // 3. 向量化 + writeVectorStore(getVectorStoreById(knowledge), segment, new Document(segment.getContent())); + return segment.getId(); + } + + @Override + public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) { + return validateKnowledgeSegmentExists(id); + } + } From 61ea09488ed624d1f15fc01b8ffa98aa717484b7 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 3 Mar 2025 12:22:19 +0800 Subject: [PATCH 234/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A1=A5=E6=A2=81=E6=89=A7=E8=A1=8C=E5=99=A8=E6=8A=BD=E8=B1=A1?= =?UTF-8?q?=E7=B1=BB=E5=A2=9E=E5=8A=A0=E6=B3=9B=E5=9E=8B=EF=BC=8C=E5=87=8F?= =?UTF-8?q?=E5=B0=91=E5=AD=90=E7=B1=BB=E7=B1=BB=E5=9E=8B=E5=BC=BA=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractCacheableDataBridgeExecute.java | 50 +++++++++++++------ .../IotKafkaMQDataBridgeExecute.java | 24 ++++----- .../IotRabbitMQDataBridgeExecute.java | 41 +++++++-------- .../IotRedisStreamMQDataBridgeExecute.java | 32 +++++------- .../IotRocketMQDataBridgeExecute.java | 18 +++---- 5 files changed, 81 insertions(+), 84 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index ebd0f87764..ec0c8bea97 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.cache.RemovalListener; import lombok.extern.slf4j.Slf4j; import java.time.Duration; @@ -15,22 +16,36 @@ import java.time.Duration; /** * 带缓存功能的数据桥梁执行器抽象类 * + * 该类提供了一个通用的缓存机制,用于管理各类数据桥接的生产者(Producer)实例。 + * + * 主要特点: + * - 基于Guava Cache实现高效的生产者实例缓存管理 + * - 自动处理生产者的生命周期(创建、获取、关闭) + * - 支持30分钟未访问自动过期清理机制 + * - 异常处理与日志记录,便于问题排查 + * + * 子类需要实现: + * - initProducer(Config) - 初始化特定类型的生产者实例 + * - closeProducer(Producer) - 关闭生产者实例并释放资源 + * + * @param 配置信息类型,用于初始化生产者 + * @param 生产者类型,负责将数据发送到目标系统 * @author HUIHUI */ @Slf4j -public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { +public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { - // TODO @huihui:AbstractCacheableDataBridgeExecute 这样,下面的 Object, Object 就有了类型;另外 IotDataBridgeDO.Config 可以替代一个 Object 哇, /** * Producer 缓存 */ - private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() - .expireAfterAccess(Duration.ofMinutes(30)) - .removalListener(notification -> { - Object producer = notification.getValue(); + private final LoadingCache PRODUCER_CACHE = CacheBuilder.newBuilder() + .expireAfterAccess(Duration.ofMinutes(30)) // 30 分钟未访问就提前过期 + .removalListener((RemovalListener) notification -> { + Producer producer = notification.getValue(); if (producer == null) { return; } + try { closeProducer(producer); log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已关闭]", notification.getKey()); @@ -38,15 +53,18 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 关闭失败]", notification.getKey(), e); } }) - .build(new CacheLoader() { - + .build(new CacheLoader() { @Override - public Object load(Object config) throws Exception { - Object producer = initProducer(config); - log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); - return producer; + public Producer load(Config config) throws Exception { + try { + Producer producer = initProducer(config); + log.info("[PRODUCER_CACHE][配置({}) 对应的 producer 已创建并启动]", config); + return producer; + } catch (Exception e) { + log.error("[PRODUCER_CACHE][配置({}) 对应的 producer 创建启动失败]", config, e); + throw e; // 抛出异常,触发缓存加载失败机制 + } } - }); /** @@ -55,7 +73,7 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg * @param config 配置信息 * @return 生产者对象 */ - protected Object getProducer(Object config) throws Exception { + protected Producer getProducer(Config config) throws Exception { return PRODUCER_CACHE.get(config); } @@ -66,13 +84,13 @@ public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridg * @return 生产者对象 * @throws Exception 如果初始化失败 */ - protected abstract Object initProducer(Object config) throws Exception; + protected abstract Producer initProducer(Config config) throws Exception; /** * 关闭生产者 * * @param producer 生产者对象 */ - protected abstract void closeProducer(Object producer); + protected abstract void closeProducer(Producer producer) throws Exception; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index e95d4ced95..33aa744a85 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -24,7 +24,8 @@ import java.util.concurrent.TimeoutException; */ @Component @Slf4j -public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { +public class IotKafkaMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute> { private static final Duration SEND_TIMEOUT = Duration.ofMillis(10); @@ -38,11 +39,10 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig()); } - @SuppressWarnings("unchecked") private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) { try { // 1. 获取或创建 KafkaTemplate - KafkaTemplate kafkaTemplate = (KafkaTemplate) getProducer(config); + KafkaTemplate kafkaTemplate = getProducer(config); // 2. 发送消息并等待结果 kafkaTemplate.send(config.getTopic(), message.toString()) @@ -56,24 +56,22 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec } @Override - protected Object initProducer(Object config) { - IotDataBridgeDO.KafkaMQConfig kafkaConfig = (IotDataBridgeDO.KafkaMQConfig) config; - + protected KafkaTemplate initProducer(IotDataBridgeDO.KafkaMQConfig config) { // 1.1 构建生产者配置 Map props = new HashMap<>(); - props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfig.getBootstrapServers()); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); // 1.2 如果配置了认证信息 - if (kafkaConfig.getUsername() != null && kafkaConfig.getPassword() != null) { + if (config.getUsername() != null && config.getPassword() != null) { props.put("security.protocol", "SASL_PLAINTEXT"); props.put("sasl.mechanism", "PLAIN"); props.put("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"" - + kafkaConfig.getUsername() + "\" password=\"" + kafkaConfig.getPassword() + "\";"); + + config.getUsername() + "\" password=\"" + config.getPassword() + "\";"); } // 1.3 如果启用 SSL - if (Boolean.TRUE.equals(kafkaConfig.getSsl())) { + if (Boolean.TRUE.equals(config.getSsl())) { props.put("security.protocol", "SSL"); } @@ -83,10 +81,8 @@ public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExec } @Override - protected void closeProducer(Object producer) { - if (producer instanceof KafkaTemplate) { - ((KafkaTemplate) producer).destroy(); - } + protected void closeProducer(KafkaTemplate producer) { + producer.destroy(); } // TODO @芋艿:测试代码,后续清理 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 5611873155..0918a5546d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -19,7 +19,8 @@ import java.time.LocalDateTime; */ @Component @Slf4j -public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { +public class IotRabbitMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute { @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { @@ -34,7 +35,7 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe private void executeRabbitMQ(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) { try { // 1. 获取或创建 Channel - Channel channel = (Channel) getProducer(config); + Channel channel = getProducer(config); // 2.1 声明交换机、队列和绑定关系 channel.exchangeDeclare(config.getExchange(), "direct", true); @@ -52,16 +53,14 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe @Override @SuppressWarnings("resource") - protected Object initProducer(Object config) throws Exception { - IotDataBridgeDO.RabbitMQConfig rabbitConfig = (IotDataBridgeDO.RabbitMQConfig) config; - + protected Channel initProducer(IotDataBridgeDO.RabbitMQConfig config) throws Exception { // 1. 创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); - factory.setHost(rabbitConfig.getHost()); - factory.setPort(rabbitConfig.getPort()); - factory.setVirtualHost(rabbitConfig.getVirtualHost()); - factory.setUsername(rabbitConfig.getUsername()); - factory.setPassword(rabbitConfig.getPassword()); + factory.setHost(config.getHost()); + factory.setPort(config.getPort()); + factory.setVirtualHost(config.getVirtualHost()); + factory.setUsername(config.getUsername()); + factory.setPassword(config.getPassword()); // 2. 创建连接 Connection connection = factory.newConnection(); @@ -71,20 +70,13 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe } @Override - protected void closeProducer(Object producer) { - if (producer instanceof Channel) { - try { - Channel channel = (Channel) producer; - if (channel.isOpen()) { - channel.close(); - } - Connection connection = channel.getConnection(); - if (connection.isOpen()) { - connection.close(); - } - } catch (Exception e) { - log.error("[closeProducer][关闭 RabbitMQ 连接异常]", e); - } + protected void closeProducer(Channel channel) throws Exception { + if (channel.isOpen()) { + channel.close(); + } + Connection connection = channel.getConnection(); + if (connection.isOpen()) { + connection.close(); } } @@ -124,4 +116,5 @@ public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExe log.info("[main][第二次执行,应该会复用缓存的 channel]"); action.executeRabbitMQ(message, config); } + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java index 552fc3425b..77b688606a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -29,7 +29,8 @@ import java.time.LocalDateTime; */ @Component @Slf4j -public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { +public class IotRedisStreamMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute> { @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { @@ -46,7 +47,7 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid private void executeRedisStream(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) { try { // 1. 获取 RedisTemplate - RedisTemplate redisTemplate = (RedisTemplate) getProducer(config); + RedisTemplate redisTemplate = getProducer(config); // 2. 创建并发送 Stream 记录 ObjectRecord record = StreamRecords.newRecord() @@ -59,17 +60,15 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid } @Override - protected Object initProducer(Object config) { - IotDataBridgeDO.RedisStreamMQConfig redisConfig = (IotDataBridgeDO.RedisStreamMQConfig) config; - + protected RedisTemplate initProducer(IotDataBridgeDO.RedisStreamMQConfig config) { // 1.1 创建 Redisson 配置 Config redissonConfig = new Config(); SingleServerConfig serverConfig = redissonConfig.useSingleServer() - .setAddress("redis://" + redisConfig.getHost() + ":" + redisConfig.getPort()) - .setDatabase(redisConfig.getDatabase()); + .setAddress("redis://" + config.getHost() + ":" + config.getPort()) + .setDatabase(config.getDatabase()); // 1.2 设置密码(如果有) - if (StrUtil.isNotBlank(redisConfig.getPassword())) { - serverConfig.setPassword(redisConfig.getPassword()); + if (StrUtil.isNotBlank(config.getPassword())) { + serverConfig.setPassword(config.getPassword()); } // TODO @huihui:看看能不能简化一些。按道理说,不用这么多的哈。 @@ -90,17 +89,10 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBrid } @Override - protected void closeProducer(Object producer) { - // TODO @huihui:try catch 交给父类来做。子类不处理异常 - if (producer instanceof RedisTemplate) { - RedisConnectionFactory factory = ((RedisTemplate) producer).getConnectionFactory(); - try { - if (factory != null) { - ((RedissonConnectionFactory) factory).destroy(); - } - } catch (Exception e) { - log.error("[closeProducer][关闭 redisson 连接异常]", e); - } + protected void closeProducer(RedisTemplate producer) throws Exception { + RedisConnectionFactory factory = producer.getConnectionFactory(); + if (factory != null) { + ((RedissonConnectionFactory) factory).destroy(); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index 3b53252539..061bbfc69f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -20,7 +20,8 @@ import java.time.LocalDateTime; */ @Component @Slf4j -public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { +public class IotRocketMQDataBridgeExecute extends + AbstractCacheableDataBridgeExecute { @Override public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { @@ -35,7 +36,7 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { try { // 1. 获取或创建 Producer - DefaultMQProducer producer = (DefaultMQProducer) getProducer(config); + DefaultMQProducer producer = getProducer(config); // 2.1 创建消息对象,指定Topic、Tag和消息体 Message msg = new Message( @@ -57,19 +58,16 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExe } @Override - protected Object initProducer(Object config) throws Exception { - IotDataBridgeDO.RocketMQConfig rocketMQConfig = (IotDataBridgeDO.RocketMQConfig) config; - DefaultMQProducer producer = new DefaultMQProducer(rocketMQConfig.getGroup()); - producer.setNamesrvAddr(rocketMQConfig.getNameServer()); + protected DefaultMQProducer initProducer(IotDataBridgeDO.RocketMQConfig config) throws Exception { + DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); + producer.setNamesrvAddr(config.getNameServer()); producer.start(); return producer; } @Override - protected void closeProducer(Object producer) { - if (producer instanceof DefaultMQProducer) { - ((DefaultMQProducer) producer).shutdown(); - } + protected void closeProducer(DefaultMQProducer producer) { + producer.shutdown(); } // TODO @芋艿:测试代码,后续清理 From 3b54deb989630f21b7313ac398b4e1f417172fe4 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 3 Mar 2025 12:48:07 +0800 Subject: [PATCH 235/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A1=A5=E6=A2=81=E7=9A=84=E6=89=A7=E8=A1=8C=E5=99=A8=E5=87=8F?= =?UTF-8?q?=E5=B0=91=E5=AD=90=E7=B1=BB=E4=BB=A3=E7=A0=81=E5=86=97=E4=BD=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AbstractCacheableDataBridgeExecute.java | 2 +- .../databridge/IotDataBridgeExecute.java | 28 +++++++++++++++++-- .../databridge/IotHttpDataBridgeExecute.java | 14 ++++------ .../IotKafkaMQDataBridgeExecute.java | 16 ++++------- .../IotRabbitMQDataBridgeExecute.java | 17 +++++------ .../IotRedisStreamMQDataBridgeExecute.java | 18 ++++-------- .../IotRocketMQDataBridgeExecute.java | 16 ++++------- 7 files changed, 56 insertions(+), 55 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index ec0c8bea97..52c4483ec7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -33,7 +33,7 @@ import java.time.Duration; * @author HUIHUI */ @Slf4j -public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { +public abstract class AbstractCacheableDataBridgeExecute implements IotDataBridgeExecute { /** * Producer 缓存 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index a10f75103c..3f842f1f50 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -9,9 +9,14 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; * * @author HUIHUI */ -public interface IotDataBridgeExecute { +public interface IotDataBridgeExecute { - // TODO @huihui:要不搞个 getType?然后 execute0 由子类实现。这样,子类的 executeRedisStream ,其实就是 execute0 了。 + /** + * 获取数据桥梁类型 + * + * @return 数据桥梁类型 + */ + Integer getType(); /** * 执行数据桥梁操作 @@ -19,6 +24,23 @@ public interface IotDataBridgeExecute { * @param message 设备消息 * @param dataBridge 数据桥梁 */ - void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge); + @SuppressWarnings({"unchecked"}) + default void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥梁类型 + if (!getType().equals(dataBridge.getType())) { + return; + } + + // 1.2 执行对应的数据桥梁发送消息 + execute0(message, (Config) dataBridge.getConfig()); + } + + /** + * 【真正】执行数据桥梁操作 + * + * @param message 设备消息 + * @param config 桥梁配置 + */ + void execute0(IotDeviceMessage message, Config config); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java index 27b8bc6bba..ffe2c5b803 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java @@ -25,23 +25,19 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ */ @Component @Slf4j -public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { +public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { @Resource private RestTemplate restTemplate; @Override - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥梁的类型 == HTTP - if (!IotDataBridgTypeEnum.HTTP.getType().equals(dataBridge.getType())) { - return; - } - // 1.2 执行 HTTP 请求 - executeHttp(message, (IotDataBridgeDO.HttpConfig) dataBridge.getConfig()); + public Integer getType() { + return IotDataBridgTypeEnum.HTTP.getType(); } + @Override @SuppressWarnings({"unchecked", "deprecation"}) - private void executeHttp(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) { + public void execute0(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) { String url = null; HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); HttpEntity requestEntity = null; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 33aa744a85..1efcfe9cab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -30,16 +30,12 @@ public class IotKafkaMQDataBridgeExecute extends private static final Duration SEND_TIMEOUT = Duration.ofMillis(10); @Override - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1. 校验数据桥梁的类型 == KAFKA - if (!IotDataBridgTypeEnum.KAFKA.getType().equals(dataBridge.getType())) { - return; - } - // 2. 执行 Kafka 发送消息 - executeKafka(message, (IotDataBridgeDO.KafkaMQConfig) dataBridge.getConfig()); + public Integer getType() { + return IotDataBridgTypeEnum.KAFKA.getType(); } - private void executeKafka(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) { + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) { try { // 1. 获取或创建 KafkaTemplate KafkaTemplate kafkaTemplate = getProducer(config); @@ -113,10 +109,10 @@ public class IotKafkaMQDataBridgeExecute extends // 4. 执行两次测试,验证缓存 log.info("[main][第一次执行,应该会创建新的 producer]"); - action.executeKafka(message, config); + action.execute0(message, config); log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.executeKafka(message, config); + action.execute0(message, config); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 0918a5546d..27ebccc399 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -22,17 +22,14 @@ import java.time.LocalDateTime; public class IotRabbitMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { + @Override - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥梁的类型 == RABBITMQ - if (!IotDataBridgTypeEnum.RABBITMQ.getType().equals(dataBridge.getType())) { - return; - } - // 1.2 执行 RabbitMQ 发送消息 - executeRabbitMQ(message, (IotDataBridgeDO.RabbitMQConfig) dataBridge.getConfig()); + public Integer getType() { + return IotDataBridgTypeEnum.RABBITMQ.getType(); } - private void executeRabbitMQ(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) { + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) { try { // 1. 获取或创建 Channel Channel channel = getProducer(config); @@ -111,10 +108,10 @@ public class IotRabbitMQDataBridgeExecute extends // 4. 执行两次测试,验证缓存 log.info("[main][第一次执行,应该会创建新的 channel]"); - action.executeRabbitMQ(message, config); + action.execute0(message, config); log.info("[main][第二次执行,应该会复用缓存的 channel]"); - action.executeRabbitMQ(message, config); + action.execute0(message, config); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java index 77b688606a..f60528ef9f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -33,18 +33,12 @@ public class IotRedisStreamMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute> { @Override - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥梁类型 - if (!IotDataBridgTypeEnum.REDIS_STREAM.getType().equals(dataBridge.getType())) { - return; - } - // 1.2 执行消息发送 - executeRedisStream(message, (IotDataBridgeDO.RedisStreamMQConfig) dataBridge.getConfig()); + public Integer getType() { + return IotDataBridgTypeEnum.REDIS_STREAM.getType(); } - @SuppressWarnings("unchecked") - // TODO @huihui:try catch 交给父类来做,子类不处理异常 - private void executeRedisStream(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) { + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) { try { // 1. 获取 RedisTemplate RedisTemplate redisTemplate = getProducer(config); @@ -133,10 +127,10 @@ public class IotRedisStreamMQDataBridgeExecute extends // 4. 执行两次测试,验证缓存 log.info("[main][第一次执行,应该会创建新的 RedisTemplate]"); - action.executeRedisStream(message, config); + action.execute0(message, config); log.info("[main][第二次执行,应该会复用缓存的 RedisTemplate]"); - action.executeRedisStream(message, config); + action.execute0(message, config); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index 061bbfc69f..a68a6525e4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -24,16 +24,12 @@ public class IotRocketMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute { @Override - public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥梁的类型 == ROCKETMQ - if (!IotDataBridgTypeEnum.ROCKETMQ.getType().equals(dataBridge.getType())) { - return; - } - // 1.2 执行 RocketMQ 发送消息 - executeRocketMQ(message, (IotDataBridgeDO.RocketMQConfig) dataBridge.getConfig()); + public Integer getType() { + return IotDataBridgTypeEnum.ROCKETMQ.getType(); } - private void executeRocketMQ(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { + @Override + public void execute0(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { try { // 1. 获取或创建 Producer DefaultMQProducer producer = getProducer(config); @@ -97,10 +93,10 @@ public class IotRocketMQDataBridgeExecute extends // 4. 执行两次测试,验证缓存 log.info("[main][第一次执行,应该会创建新的 producer]"); - action.executeRocketMQ(message, config); + action.execute0(message, config); log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.executeRocketMQ(message, config); + action.execute0(message, config); } } From ce5e64e0aa635be458334af1d27236b102c6db4b Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 3 Mar 2025 13:03:41 +0800 Subject: [PATCH 236/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A1=A5=E6=A2=81=E7=9A=84=E6=89=A7=E8=A1=8C=E5=99=A8=E5=87=8F?= =?UTF-8?q?=E5=B0=91=E5=AD=90=E7=B1=BB=E4=BB=A3=E7=A0=81=E5=86=97=E4=BD=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/action/IotRuleSceneAction.java | 2 +- .../action/IotRuleSceneDataBridgeAction.java | 8 ++-- .../AbstractCacheableDataBridgeExecute.java | 18 +++++++++ .../databridge/IotDataBridgeExecute.java | 4 +- .../IotKafkaMQDataBridgeExecute.java | 25 +++++------- .../IotRabbitMQDataBridgeExecute.java | 35 ++++++++-------- .../IotRedisStreamMQDataBridgeExecute.java | 28 ++++++------- .../IotRocketMQDataBridgeExecute.java | 40 +++++++++---------- 8 files changed, 81 insertions(+), 79 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java index 4cf1f8f285..a673b538ef 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java @@ -22,7 +22,7 @@ public interface IotRuleSceneAction { * 2. 非空的情况:设备触发 * @param config 配置 */ - void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config); + void execute(@Nullable IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception; /** * 获得类型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index 6733331cb4..d94922f5d0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -26,10 +26,10 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { @Resource private IotDataBridgeService dataBridgeService; @Resource - private List dataBridgeExecutes; + private List> dataBridgeExecutes; @Override - public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) { + public void execute(IotDeviceMessage message, IotRuleSceneDO.ActionConfig config) throws Exception { // 1.1 如果消息为空,直接返回 if (message == null) { return; @@ -47,7 +47,9 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { } // 2. 执行数据桥接操作 - dataBridgeExecutes.forEach(execute -> execute.execute(message, dataBridge)); + for (IotDataBridgeExecute execute : dataBridgeExecutes) { + execute.execute(message, dataBridge); + } } @Override diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index 52c4483ec7..d26c2dd436 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -93,4 +95,20 @@ public abstract class AbstractCacheableDataBridgeExecute imple */ protected abstract void closeProducer(Producer producer) throws Exception; + @Override + @SuppressWarnings({"unchecked"}) + public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + // 1.1 校验数据桥梁类型 + if (!getType().equals(dataBridge.getType())) { + return; + } + + // 1.2 执行对应的数据桥梁发送消息 + try { + execute0(message, (Config) dataBridge.getConfig()); + } catch (Exception e) { + log.error("[execute][桥梁配置 config({}) 对应的 message({}) 发送异常]", dataBridge.getConfig(), message, e); + } + } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index 3f842f1f50..ce3d0f1938 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -25,7 +25,7 @@ public interface IotDataBridgeExecute { * @param dataBridge 数据桥梁 */ @SuppressWarnings({"unchecked"}) - default void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { + default void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) throws Exception { // 1.1 校验数据桥梁类型 if (!getType().equals(dataBridge.getType())) { return; @@ -41,6 +41,6 @@ public interface IotDataBridgeExecute { * @param message 设备消息 * @param config 桥梁配置 */ - void execute0(IotDeviceMessage message, Config config); + void execute0(IotDeviceMessage message, Config config) throws Exception; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 1efcfe9cab..b943eb31af 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -15,7 +15,6 @@ import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; /** * Kafka 的 {@link IotDataBridgeExecute} 实现类 @@ -35,20 +34,14 @@ public class IotKafkaMQDataBridgeExecute extends } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) { - try { - // 1. 获取或创建 KafkaTemplate - KafkaTemplate kafkaTemplate = getProducer(config); + public void execute0(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) throws Exception { + // 1. 获取或创建 KafkaTemplate + KafkaTemplate kafkaTemplate = getProducer(config); - // 2. 发送消息并等待结果 - kafkaTemplate.send(config.getTopic(), message.toString()) - .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待 - log.info("[executeKafka][message({}) 发送成功]", message); - } catch (TimeoutException e) { - log.error("[executeKafka][message({}) config({}) 发送超时]", message, config, e); - } catch (Exception e) { - log.error("[executeKafka][message({}) config({}) 发送异常]", message, config, e); - } + // 2. 发送消息并等待结果 + kafkaTemplate.send(config.getTopic(), message.toString()) + .get(SEND_TIMEOUT.getSeconds(), TimeUnit.SECONDS); // 添加超时等待 + log.info("[execute0][message({}) 发送成功]", message); } @Override @@ -109,10 +102,10 @@ public class IotKafkaMQDataBridgeExecute extends // 4. 执行两次测试,验证缓存 log.info("[main][第一次执行,应该会创建新的 producer]"); - action.execute0(message, config); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.execute0(message, config); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 27ebccc399..f99e651795 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -29,23 +29,19 @@ public class IotRabbitMQDataBridgeExecute extends } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) { - try { - // 1. 获取或创建 Channel - Channel channel = getProducer(config); + public void execute0(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) throws Exception { + // 1. 获取或创建 Channel + Channel channel = getProducer(config); - // 2.1 声明交换机、队列和绑定关系 - channel.exchangeDeclare(config.getExchange(), "direct", true); - channel.queueDeclare(config.getQueue(), true, false, false, null); - channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); + // 2.1 声明交换机、队列和绑定关系 + channel.exchangeDeclare(config.getExchange(), "direct", true); + channel.queueDeclare(config.getQueue(), true, false, false, null); + channel.queueBind(config.getQueue(), config.getExchange(), config.getRoutingKey()); - // 2.2 发送消息 - channel.basicPublish(config.getExchange(), config.getRoutingKey(), null, - message.toString().getBytes(StandardCharsets.UTF_8)); - log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config); - } catch (Exception e) { - log.error("[executeRabbitMQ][message({}) config({}) 发送异常]", message, config, e); - } + // 2.2 发送消息 + channel.basicPublish(config.getExchange(), config.getRoutingKey(), null, + message.toString().getBytes(StandardCharsets.UTF_8)); + log.info("[executeRabbitMQ][message({}) config({}) 发送成功]", message, config); } @Override @@ -107,11 +103,12 @@ public class IotRabbitMQDataBridgeExecute extends .build(); // 4. 执行两次测试,验证缓存 - log.info("[main][第一次执行,应该会创建新的 channel]"); - action.execute0(message, config); + // 4. 执行两次测试,验证缓存 + log.info("[main][第一次执行,应该会创建新的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - log.info("[main][第二次执行,应该会复用缓存的 channel]"); - action.execute0(message, config); + log.info("[main][第二次执行,应该会复用缓存的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java index f60528ef9f..a20334f4fd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -38,19 +38,15 @@ public class IotRedisStreamMQDataBridgeExecute extends } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) { - try { - // 1. 获取 RedisTemplate - RedisTemplate redisTemplate = getProducer(config); + public void execute0(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) throws Exception { + // 1. 获取 RedisTemplate + RedisTemplate redisTemplate = getProducer(config); - // 2. 创建并发送 Stream 记录 - ObjectRecord record = StreamRecords.newRecord() - .ofObject(message).withStreamKey(config.getTopic()); - String recordId = String.valueOf(redisTemplate.opsForStream().add(record)); - log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config); - } catch (Exception e) { - log.error("[executeRedisStream][消息发送失败] message: {}, config: {}", message, config, e); - } + // 2. 创建并发送 Stream 记录 + ObjectRecord record = StreamRecords.newRecord() + .ofObject(message).withStreamKey(config.getTopic()); + String recordId = String.valueOf(redisTemplate.opsForStream().add(record)); + log.info("[executeRedisStream][消息发送成功] messageId: {}, config: {}", recordId, config); } @Override @@ -126,11 +122,11 @@ public class IotRedisStreamMQDataBridgeExecute extends .build(); // 4. 执行两次测试,验证缓存 - log.info("[main][第一次执行,应该会创建新的 RedisTemplate]"); - action.execute0(message, config); + log.info("[main][第一次执行,应该会创建新的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - log.info("[main][第二次执行,应该会复用缓存的 RedisTemplate]"); - action.execute0(message, config); + log.info("[main][第二次执行,应该会复用缓存的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index a68a6525e4..df413c7503 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -29,27 +29,23 @@ public class IotRocketMQDataBridgeExecute extends } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) { - try { - // 1. 获取或创建 Producer - DefaultMQProducer producer = getProducer(config); + public void execute0(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) throws Exception { + // 1. 获取或创建 Producer + DefaultMQProducer producer = getProducer(config); - // 2.1 创建消息对象,指定Topic、Tag和消息体 - Message msg = new Message( - config.getTopic(), - config.getTags(), - message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) - ); - // 2.2 发送同步消息并处理结果 - SendResult sendResult = producer.send(msg); - // 2.3 处理发送结果 - if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { - log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); - } else { - log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); - } - } catch (Exception e) { - log.error("[executeRocketMQ][message({}) config({}) 发送异常]", message, config, e); + // 2.1 创建消息对象,指定Topic、Tag和消息体 + Message msg = new Message( + config.getTopic(), + config.getTags(), + message.toString().getBytes(RemotingHelper.DEFAULT_CHARSET) + ); + // 2.2 发送同步消息并处理结果 + SendResult sendResult = producer.send(msg); + // 2.3 处理发送结果 + if (SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { + log.info("[executeRocketMQ][message({}) config({}) 发送成功,结果({})]", message, config, sendResult); + } else { + log.error("[executeRocketMQ][message({}) config({}) 发送失败,结果({})]", message, config, sendResult); } } @@ -93,10 +89,10 @@ public class IotRocketMQDataBridgeExecute extends // 4. 执行两次测试,验证缓存 log.info("[main][第一次执行,应该会创建新的 producer]"); - action.execute0(message, config); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.execute0(message, config); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); } } From 89d079349c987eaa1065883433cd0e2ab6fc5bfe Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Mar 2025 21:26:31 +0800 Subject: [PATCH 237/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E2=80=9C=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=E9=87=8D=E6=9E=84=E4=B8=BA=E2=80=9C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=EF=BC=8C=E6=94=AF=E6=8C=81=20type?= =?UTF-8?q?=20=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 14 +- .../AiChatConversationRespVO.java | 4 +- .../admin/image/vo/AiImageDrawReqVO.java | 9 +- .../admin/model/AiApiKeyController.java | 7 +- .../admin/model/AiChatModelController.java | 84 ----------- .../admin/model/AiModelController.java | 89 ++++++++++++ .../model/vo/chatRole/AiChatRoleRespVO.java | 4 +- .../AiModelPageReqVO.java} | 6 +- .../AiModelRespVO.java} | 9 +- .../AiModelSaveReqVO.java} | 19 ++- .../dataobject/chat/AiChatConversationDO.java | 6 +- .../dal/dataobject/chat/AiChatMessageDO.java | 6 +- .../ai/dal/dataobject/image/AiImageDO.java | 13 +- .../dataobject/knowledge/AiKnowledgeDO.java | 6 +- .../dal/dataobject/mindmap/AiMindMapDO.java | 8 +- .../ai/dal/dataobject/model/AiChatRoleDO.java | 2 +- .../{AiChatModelDO.java => AiModelDO.java} | 18 ++- .../ai/dal/dataobject/write/AiWriteDO.java | 17 ++- .../ai/dal/mysql/model/AiChatMapper.java | 47 +++++++ .../ai/dal/mysql/model/AiChatModelMapper.java | 43 ------ .../chat/AiChatConversationServiceImpl.java | 19 +-- .../chat/AiChatMessageServiceImpl.java | 23 ++- .../ai/service/image/AiImageServiceImpl.java | 72 ++++++---- .../AiKnowledgeSegmentServiceImpl.java | 18 ++- .../knowledge/AiKnowledgeServiceImpl.java | 10 +- .../service/mindmap/AiMindMapServiceImpl.java | 28 ++-- .../ai/service/model/AiApiKeyService.java | 51 +------ .../ai/service/model/AiApiKeyServiceImpl.java | 74 +--------- .../ai/service/model/AiChatModelService.java | 92 ------------ .../service/model/AiChatModelServiceImpl.java | 128 ++++++++++++----- .../ai/service/model/AiModelService.java | 131 ++++++++++++++++++ .../ai/service/music/AiMusicServiceImpl.java | 8 +- .../ai/service/write/AiWriteServiceImpl.java | 32 ++--- .../ai/core/enums/AiModelTypeEnum.java | 41 ++++++ .../ai/core/enums/AiPlatformEnum.java | 12 +- .../ai/core/factory/AiModelFactoryImpl.java | 21 --- 36 files changed, 617 insertions(+), 554 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/{chatModel/AiChatModelPageReqVO.java => model/AiModelPageReqVO.java} (67%) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/{chatModel/AiChatModelRespVO.java => model/AiModelRespVO.java} (83%) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/{chatModel/AiChatModelSaveReqVO.java => model/AiModelSaveReqVO.java} (71%) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/{AiChatModelDO.java => AiModelDO.java} (77%) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiModelTypeEnum.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 2b236e6f6a..205fc80b10 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -12,31 +12,25 @@ public interface ErrorCodeConstants { // ========== API 密钥 1-040-000-000 ========== ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在"); ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!"); - ErrorCode API_KEY_MIDJOURNEY_NOT_FOUND = new ErrorCode(1_040_000_900, "Midjourney 模型不存在"); - ErrorCode API_KEY_SUNO_NOT_FOUND = new ErrorCode(1_040_000_901, "Suno 模型不存在"); - ErrorCode API_KEY_IMAGE_NODE_FOUND = new ErrorCode(1_040_000_902, "平台({}) 图片模型未配置"); - // ========== API 聊天模型 1-040-001-000 ========== - ErrorCode CHAT_MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!"); - ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!"); - ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型"); + // ========== API 模型 1-040-001-000 ========== + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!"); + ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!"); + ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型"); // ========== API 聊天角色 1-040-002-000 ========== ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!"); // ========== API 聊天会话 1-040-003-000 ========== - ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!"); ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整"); // ========== API 聊天消息 1-040-004-000 ========== - ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!"); ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!"); // ========== API 绘画 1-040-005-000 ========== - ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!"); ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}"); ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java index 66eb24db5f..7da37ebc9b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import com.fhs.core.trans.anno.Trans; import com.fhs.core.trans.constant.TransType; @@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO { private Long roleId; @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName") + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName") private Long modelId; @Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java index a38935ef71..02225ea496 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java @@ -14,18 +14,15 @@ import java.util.Map; @Data public class AiImageDrawReqVO { - @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") - private String platform; // 参见 AiPlatformEnum 枚举 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "模型编号不能为空") + private Long modelId; @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城") @NotEmpty(message = "提示词不能为空") @Size(max = 1200, message = "提示词最大 1200") private String prompt; - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6") - @NotEmpty(message = "模型不能为空") - private String model; - /** * 1. dall-e-2 模型:256x256、512x512、1024x1024 * 2. dall-e-3 模型:1024x1024, 1792x1024, 或 1024x1792 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java index 2bc190051d..c109b033ca 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java @@ -6,9 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -76,9 +75,9 @@ public class AiApiKeyController { @GetMapping("/simple-list") @Operation(summary = "获得 API 密钥分页列表") - public CommonResult> getApiKeySimpleList() { + public CommonResult> getApiKeySimpleList() { List list = apiKeyService.getApiKeyList(); - return success(convertList(list, key -> new AiChatModelRespVO().setId(key.getId()).setName(key.getName()))); + return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName()))); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java deleted file mode 100644 index 08a53b286b..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - AI 聊天模型") -@RestController -@RequestMapping("/ai/chat-model") -@Validated -public class AiChatModelController { - - @Resource - private AiChatModelService chatModelService; - - @PostMapping("/create") - @Operation(summary = "创建聊天模型") - @PreAuthorize("@ss.hasPermission('ai:chat-model:create')") - public CommonResult createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) { - return success(chatModelService.createChatModel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新聊天模型") - @PreAuthorize("@ss.hasPermission('ai:chat-model:update')") - public CommonResult updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) { - chatModelService.updateChatModel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除聊天模型") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('ai:chat-model:delete')") - public CommonResult deleteChatModel(@RequestParam("id") Long id) { - chatModelService.deleteChatModel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得聊天模型") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')") - public CommonResult getChatModel(@RequestParam("id") Long id) { - AiChatModelDO chatModel = chatModelService.getChatModel(id); - return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得聊天模型分页") - @PreAuthorize("@ss.hasPermission('ai:chat-model:query')") - public CommonResult> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) { - PageResult pageResult = chatModelService.getChatModelPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获得聊天模型列表") - @Parameter(name = "status", description = "状态", required = true, example = "1") - public CommonResult> getChatModelSimpleList(@RequestParam("status") Integer status) { - List list = chatModelService.getChatModelListByStatus(status); - return success(convertList(list, model -> new AiChatModelRespVO().setId(model.getId()) - .setName(model.getName()).setModel(model.getModel()))); - } - -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java new file mode 100644 index 0000000000..86dd4d0a61 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - AI 模型") +@RestController +@RequestMapping("/ai/model") +@Validated +public class AiModelController { + + @Resource + private AiModelService modelService; + + @PostMapping("/create") + @Operation(summary = "创建模型") + @PreAuthorize("@ss.hasPermission('ai:model:create')") + public CommonResult createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) { + return success(modelService.createModel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新模型") + @PreAuthorize("@ss.hasPermission('ai:model:update')") + public CommonResult updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) { + modelService.updateModel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:model:delete')") + public CommonResult deleteModel(@RequestParam("id") Long id) { + modelService.deleteModel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:model:query')") + public CommonResult getModel(@RequestParam("id") Long id) { + AiModelDO model = modelService.getModel(id); + return success(BeanUtils.toBean(model, AiModelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得模型分页") + @PreAuthorize("@ss.hasPermission('ai:model:query')") + public CommonResult> getModelPage(@Valid AiModelPageReqVO pageReqVO) { + PageResult pageResult = modelService.getModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiModelRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得模型列表") + @Parameter(name = "type", description = "类型", required = true, example = "1") + @Parameter(name = "platform", description = "平台", example = "midjourney") + public CommonResult> getModelSimpleList( + @RequestParam("type") Integer type, + @RequestParam(value = "platform", required = false) String platform) { + List list = modelService.getModelListByStatusAndType( + CommonStatusEnum.ENABLE.getStatus(), type, platform); + return success(convertList(list, model -> new AiModelRespVO().setId(model.getId()) + .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform()))); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java index eb34da2748..d47c66d2bd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import com.fhs.core.trans.anno.Trans; import com.fhs.core.trans.constant.TransType; import com.fhs.core.trans.vo.VO; @@ -20,7 +20,7 @@ public class AiChatRoleRespVO implements VO { private Long userId; @Schema(description = "模型编号", example = "17640") - @Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"}) + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"}) private Long modelId; @Schema(description = "模型名字", example = "张三") private String modelName; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java similarity index 67% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java index ce2f83b4b1..af8d1121a4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java @@ -1,12 +1,12 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; import lombok.*; import io.swagger.v3.oas.annotations.media.Schema; import cn.iocoder.yudao.framework.common.pojo.PageParam; -@Schema(description = "管理后台 - API 聊天模型分页 Request VO") +@Schema(description = "管理后台 - API 模型分页 Request VO") @Data -public class AiChatModelPageReqVO extends PageParam { +public class AiModelPageReqVO extends PageParam { @Schema(description = "模型名字", example = "张三") private String name; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java similarity index 83% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java index 681dabe687..b50b70a087 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java @@ -1,13 +1,13 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -@Schema(description = "管理后台 - AI 聊天模型 Response VO") +@Schema(description = "管理后台 - AI 模型 Response VO") @Data -public class AiChatModelRespVO { +public class AiModelRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630") private Long id; @@ -24,6 +24,9 @@ public class AiChatModelRespVO { @Schema(description = "模型平台", example = "OpenAI") private String platform; + @Schema(description = "模型类型", example = "1") + private Integer type; + @Schema(description = "排序", example = "1") private Integer sort; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java similarity index 71% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java index 4fad5a1fc1..281bcd6732 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java @@ -1,14 +1,17 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import jakarta.validation.constraints.*; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; -@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO") +@Schema(description = "管理后台 - API 模型新增/修改 Request VO") @Data -public class AiChatModelSaveReqVO { +public class AiModelSaveReqVO { @Schema(description = "编号", example = "2630") private Long id; @@ -27,8 +30,14 @@ public class AiChatModelSaveReqVO { @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") @NotEmpty(message = "模型平台不能为空") + @InEnum(AiPlatformEnum.class) private String platform; + @Schema(description = "模型类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "模型类型不能为空") + @InEnum(AiModelTypeEnum.class) + private Integer type; + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "排序不能为空") private Integer sort; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java index b5e9d06541..a9c956deae 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -76,13 +76,13 @@ public class AiChatConversationDO extends BaseDO { /** * 模型编号 * - * 关联 {@link AiChatModelDO#getId()} 字段 + * 关联 {@link AiModelDO#getId()} 字段 */ private Long modelId; /** * 模型标志 * - * 冗余 {@link AiChatModelDO#getModel()} 字段 + * 冗余 {@link AiModelDO#getModel()} 字段 */ private String model; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java index c65360ccbb..a026ec8193 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; @@ -83,13 +83,13 @@ public class AiChatMessageDO extends BaseDO { /** * 模型标志 * - * 冗余 {@link AiChatModelDO#getModel()} + * 冗余 {@link AiModelDO#getModel()} */ private String model; /** * 模型编号 * - * 关联 {@link AiChatModelDO#getId()} 字段 + * 关联 {@link AiModelDO#getId()} 字段 */ private Long modelId; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java index 5b43bc91e9..a18904c022 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.image; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -52,11 +52,16 @@ public class AiImageDO extends BaseDO { * 枚举 {@link cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum} */ private String platform; - // TODO @芋艿:modelId? /** - * 模型 + * 模型编号 * - * 冗余 {@link AiChatModelDO#getModel()} + * 关联 {@link AiModelDO#getId()} + */ + private Long modelId; + /** + * 模型标识 + * + * 冗余 {@link AiModelDO#getModel()} */ private String model; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java index 732d54e24a..e1327a50ef 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -35,13 +35,13 @@ public class AiKnowledgeDO extends BaseDO { /** * 向量模型编号 * - * 关联 {@link AiChatModelDO#getId()} + * 关联 {@link AiModelDO#getId()} */ private Long embeddingModelId; /** * 模型标识 * - * 冗余 {@link AiChatModelDO#getModel()} + * 冗余 {@link AiModelDO#getModel()} */ private String embeddingModel; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java index 47aa0ce4a5..6dd5d44302 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -36,7 +37,12 @@ public class AiMindMapDO extends BaseDO { * 枚举 {@link AiPlatformEnum} */ private String platform; - // TODO @芋艿:modelId? + /** + * 模型编号 + * + * 关联 {@link AiModelDO#getId()} + */ + private Long modelId; /** * 模型 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java index f5ed533a92..fd35b85795 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java @@ -58,7 +58,7 @@ public class AiChatRoleDO extends BaseDO { /** * 模型编号 * - * 关联 {@link AiChatModelDO#getId()} 字段 + * 关联 {@link AiModelDO#getId()} 字段 */ private Long modelId; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java similarity index 77% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java index c3800bd4ca..f4ff09212a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatModelDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model; +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; @@ -8,23 +9,22 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -// TODO @芋艿,需要改造,增加 type /** - * AI 聊天模型 DO + * AI 模型 DO * - * 默认聊天模型:{@link #status} 为开启,并且 {@link #sort} 排序第一 + * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一 * * @author fansili * @since 2024/4/24 19:39 */ -@TableName("ai_chat_model") -@KeySequence("ai_chat_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@TableName("ai_model") +@KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor -public class AiChatModelDO extends BaseDO { +public class AiModelDO extends BaseDO { /** * 编号 @@ -51,6 +51,12 @@ public class AiChatModelDO extends BaseDO { * 枚举 {@link AiPlatformEnum} */ private String platform; + /** + * 类型 + * + * 枚举 {@link AiModelTypeEnum} + */ + private Integer type; /** * 排序值 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java index a99f94e82d..e07f994aad 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.write; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; +import cn.iocoder.yudao.module.ai.enums.DictTypeConstants; import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -44,7 +46,12 @@ public class AiWriteDO extends BaseDO { * 枚举 {@link AiPlatformEnum} */ private String platform; - // TODO @芋艿:modelId? + /** + * 模型编号 + * + * 关联 {@link AiModelDO#getId()} + */ + private Long modelId; /** * 模型 */ @@ -67,25 +74,25 @@ public class AiWriteDO extends BaseDO { /** * 长度提示词 * - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LENGTH} + * 字典:{@link DictTypeConstants#AI_WRITE_LENGTH} */ private Integer length; /** * 格式提示词 * - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_FORMAT} + * 字典:{@link DictTypeConstants#AI_WRITE_FORMAT} */ private Integer format; /** * 语气提示词 * - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_TONE} + * 字典:{@link DictTypeConstants#AI_WRITE_TONE} */ private Integer tone; /** * 语言提示词 * - * 字典:{@link cn.iocoder.yudao.module.ai.enums.DictTypeConstants#AI_WRITE_LANGUAGE} + * 字典:{@link DictTypeConstants#AI_WRITE_LANGUAGE} */ private Integer language; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java new file mode 100644 index 0000000000..bfe2caf52a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; +import org.apache.ibatis.annotations.Mapper; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * API 模型 Mapper + * + * @author fansili + */ +@Mapper +public interface AiChatMapper extends BaseMapperX { + + default AiModelDO selectFirstByStatus(Integer type, Integer status) { + return selectOne(new QueryWrapperX() + .eq("type", type) + .eq("status", status) + .limitN(1) + .orderByAsc("sort")); + } + + default PageResult selectPage(AiModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiModelDO::getName, reqVO.getName()) + .eqIfPresent(AiModelDO::getModel, reqVO.getModel()) + .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform()) + .orderByAsc(AiModelDO::getSort)); + } + + default List selectListByStatusAndType(Integer status, Integer type, + @Nullable String platform) { + return selectList(new LambdaQueryWrapperX() + .eq(AiModelDO::getStatus, status) + .eq(AiModelDO::getType, type) + .eqIfPresent(AiModelDO::getPlatform, platform) + .orderByAsc(AiModelDO::getSort)); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java deleted file mode 100644 index a3136fa9f6..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatModelMapper.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql.model; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * API 聊天模型 Mapper - * - * @author fansili - */ -@Mapper -public interface AiChatModelMapper extends BaseMapperX { - - default AiChatModelDO selectFirstByStatus(Integer status) { - return selectOne(new QueryWrapperX() - .eq("status", status) - .limitN(1) - .orderByAsc("sort")); - } - - default PageResult selectPage(AiChatModelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AiChatModelDO::getName, reqVO.getName()) - .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel()) - .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform()) - .orderByAsc(AiChatModelDO::getSort)); - } - - default List selectList(Integer status) { - return selectList(new LambdaQueryWrapperX() - .eq(AiChatModelDO::getStatus, status) - .orderByAsc(AiChatModelDO::getSort)); - } - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java index 8f094087f1..792a8ae150 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java @@ -4,17 +4,18 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -44,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService private AiChatConversationMapper chatConversationMapper; @Resource - private AiChatModelService chatModalService; + private AiModelService chatModalService; @Resource private AiChatRoleService chatRoleService; @Resource @@ -54,9 +55,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) { // 1.1 获得 AiChatRoleDO 聊天角色 AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null; - // 1.2 获得 AiChatModelDO 聊天模型 - AiChatModelDO model = role != null && role.getModelId() != null ? chatModalService.validateChatModel(role.getModelId()) - : chatModalService.getRequiredDefaultChatModel(); + // 1.2 获得 AiModelDO 聊天模型 + AiModelDO model = role != null && role.getModelId() != null ? chatModalService.validateModel(role.getModelId()) + : chatModalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); Assert.notNull(model, "必须找到默认模型"); validateChatModel(model); @@ -86,9 +87,9 @@ public class AiChatConversationServiceImpl implements AiChatConversationService throw exception(CHAT_CONVERSATION_NOT_EXISTS); } // 1.2 校验模型是否存在(修改模型的情况) - AiChatModelDO model = null; + AiModelDO model = null; if (updateReqVO.getModelId() != null) { - model = chatModalService.validateChatModel(updateReqVO.getModelId()); + model = chatModalService.validateModel(updateReqVO.getModelId()); } // 1.3 校验知识库是否存在 @@ -139,7 +140,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService chatConversationMapper.deleteById(id); } - private void validateChatModel(AiChatModelDO model) { + private void validateChatModel(AiModelDO model) { if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) { return; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 1c8927e07a..2298d9b1d9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -15,13 +15,12 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.messages.Message; @@ -63,9 +62,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @Resource private AiChatConversationService chatConversationService; @Resource - private AiChatModelService chatModalService; - @Resource - private AiApiKeyService apiKeyService; + private AiModelService modalService; @Resource private AiKnowledgeSegmentService knowledgeSegmentService; @@ -78,8 +75,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } List historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); // 1.2 校验模型 - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId()); - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); + AiModelDO model = modalService.validateModel(conversation.getModelId()); + ChatModel chatModel = modalService.getChatModel(model.getKeyId()); // 2. 插入 user 发送消息 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, @@ -112,8 +109,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } List historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); // 1.2 校验模型 - AiChatModelDO model = chatModalService.validateChatModel(conversation.getModelId()); - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); + AiModelDO model = modalService.validateModel(conversation.getModelId()); + StreamingChatModel chatModel = modalService.getChatModel(model.getKeyId()); // 2. 插入 user 发送消息 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, @@ -161,8 +158,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { return null; } - private Prompt buildPrompt(AiChatConversationDO conversation, List messages,List segmentList, - AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) { + private Prompt buildPrompt(AiChatConversationDO conversation, List messages, List segmentList, + AiModelDO model, AiChatMessageSendReqVO sendReqVO) { // 1. 构建 Prompt Message 列表 List chatMessages = new ArrayList<>(); @@ -232,7 +229,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, - AiChatModelDO model, Long userId, Long roleId, + AiModelDO model, Long userId, Long roleId, MessageType messageType, String content, Boolean useContext) { AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java index 523014d530..7204f4d5b2 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java @@ -12,13 +12,17 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO; import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import cn.iocoder.yudao.module.infra.api.file.FileApi; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; import jakarta.annotation.Resource; @@ -54,15 +58,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; @Slf4j public class AiImageServiceImpl implements AiImageService { + @Resource + private AiModelService modelService; + @Resource private AiImageMapper imageMapper; @Resource private FileApi fileApi; - @Resource - private AiApiKeyService apiKeyService; - @Override public PageResult getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) { return imageMapper.selectPageMy(userId, pageReqVO); @@ -88,23 +92,31 @@ public class AiImageServiceImpl implements AiImageService { @Override public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) { - // 1. 保存数据库 - AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) - .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); + // 1. 校验模型 + AiModelDO model = modelService.validateModel(drawReqVO.getModelId()); + + // 2. 保存数据库 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId) + .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel()) + .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); imageMapper.insert(image); - // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果 - getSelf().executeDrawImage(image, drawReqVO); + + // 3. 异步绘制,后续前端通过返回的 id 进行轮询结果 + getSelf().executeDrawImage(image, drawReqVO, model); return image.getId(); } @Async - public void executeDrawImage(AiImageDO image, AiImageDrawReqVO req) { + public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) { try { // 1.1 构建请求 - ImageOptions request = buildImageOptions(req); + ImageOptions request = buildImageOptions(reqVO, model); // 1.2 执行请求 - ImageModel imageModel = apiKeyService.getImageModel(AiPlatformEnum.validatePlatform(req.getPlatform())); - ImageResponse response = imageModel.call(new ImagePrompt(req.getPrompt(), request)); + ImageModel imageModel = modelService.getImageModel(model.getId()); + ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request)); + if (response.getResult() == null) { + throw new IllegalArgumentException("生成结果为空"); + } // 2. 上传到文件服务 String b64Json = response.getResult().getOutput().getB64Json(); @@ -116,25 +128,25 @@ public class AiImageServiceImpl implements AiImageService { imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus()) .setPicUrl(filePath).setFinishTime(LocalDateTime.now())); } catch (Exception ex) { - log.error("[doDall][image({}) 生成异常]", image, ex); + log.error("[executeDrawImage][image({}) 生成异常]", image, ex); imageMapper.updateById(new AiImageDO().setId(image.getId()) .setStatus(AiImageStatusEnum.FAIL.getStatus()) .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now())); } } - private static ImageOptions buildImageOptions(AiImageDrawReqVO draw) { - if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { + private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) { + if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { // https://platform.openai.com/docs/api-reference/images/create - return OpenAiImageOptions.builder().withModel(draw.getModel()) + return OpenAiImageOptions.builder().withModel(model.getModel()) .withHeight(draw.getHeight()).withWidth(draw.getWidth()) .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 .withResponseFormat("b64_json") .build(); - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage - return StabilityAiImageOptions.builder().model(draw.getModel()) + return StabilityAiImageOptions.builder().model(model.getModel()) .height(draw.getHeight()).width(draw.getWidth()) .seed(Long.valueOf(draw.getOptions().get("seed"))) .cfgScale(Float.valueOf(draw.getOptions().get("scale"))) @@ -143,22 +155,22 @@ public class AiImageServiceImpl implements AiImageService { .stylePreset(String.valueOf(draw.getOptions().get("stylePreset"))) .clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset"))) .build(); - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) { + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) { return DashScopeImageOptions.builder() - .withModel(draw.getModel()).withN(1) + .withModel(model.getModel()).withN(1) .withHeight(draw.getHeight()).withWidth(draw.getWidth()) .build(); - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) { + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) { return QianFanImageOptions.builder() - .model(draw.getModel()).N(1) + .model(model.getModel()).N(1) .height(draw.getHeight()).width(draw.getWidth()) .build(); - } else if (ObjUtil.equal(draw.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) { + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) { return ZhiPuAiImageOptions.builder() - .model(draw.getModel()) + .model(model.getModel()) .build(); } - throw new IllegalArgumentException("不支持的 AI 平台:" + draw.getPlatform()); + throw new IllegalArgumentException("不支持的 AI 平台:" + model.getPlatform()); } @Override @@ -206,7 +218,7 @@ public class AiImageServiceImpl implements AiImageService { @Override @Transactional(rollbackFor = Exception.class) public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) { - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi(); + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(); // 1. 保存数据库 AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) @@ -237,7 +249,7 @@ public class AiImageServiceImpl implements AiImageService { @Override public Integer midjourneySync() { - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi(); + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(); // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image List imageList = imageMapper.selectListByStatusAndPlatform( AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform()); @@ -308,7 +320,7 @@ public class AiImageServiceImpl implements AiImageService { @Override public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) { - MidjourneyApi midjourneyApi = apiKeyService.getMidjourneyApi(); + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(); // 1.1 检查 image AiImageDO image = validateImageExists(reqVO.getId()); if (ObjUtil.notEqual(userId, image.getUserId())) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index adf1616cb0..910ff1249a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -7,14 +7,17 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; @@ -33,8 +36,8 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS; /** * AI 知识库分片 Service 实现类 @@ -58,7 +61,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @Lazy // 延迟加载,避免循环依赖 private AiKnowledgeDocumentService knowledgeDocumentService; @Resource - private AiApiKeyService apiKeyService; + private AiModelService modelService; @Resource private TokenCountEstimator tokenCountEstimator; @@ -180,7 +183,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqBO.getKnowledgeId()); // 2.1 向量检索 - VectorStore vectorStore = apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId()); + VectorStore vectorStore = getVectorStoreById(knowledge); List documents = vectorStore.similaritySearch(SearchRequest.builder() .query(reqBO.getContent()) .topK(ObjUtil.defaultIfNull(reqBO.getTopK(), knowledge.getTopK())) @@ -251,11 +254,12 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) { - return apiKeyService.getOrCreateVectorStoreByModelId(knowledge.getEmbeddingModelId()); + return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId()); } private VectorStore getVectorStoreById(Long knowledgeId) { - return getVectorStoreById(knowledgeService.validateKnowledgeExists(knowledgeId)); + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId); + return getVectorStoreById(knowledge); } private static List splitContentByToken(String content, Integer segmentMaxTokens) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 900cfb4a17..55822796ce 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -5,9 +5,9 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -28,12 +28,12 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { private AiKnowledgeMapper knowledgeMapper; @Resource - private AiChatModelService chatModelService; + private AiModelService chatModelService; @Override public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) { // 1. 校验模型配置 - AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getEmbeddingModelId()); + AiModelDO model = chatModelService.validateModel(createReqVO.getEmbeddingModelId()); // 2. 插入知识库 AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) @@ -47,7 +47,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { // 1.1 校验知识库存在 validateKnowledgeExists(updateReqVO.getId()); // 1.2 校验模型配置 - AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getEmbeddingModelId()); + AiModelDO model = chatModelService.validateModel(updateReqVO.getEmbeddingModelId()); // 2. 更新知识库 AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java index 5536831469..3eb49441b5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.mindmap; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.util.AiUtils; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -12,14 +13,13 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper; import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.messages.Message; @@ -50,9 +50,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_E public class AiMindMapServiceImpl implements AiMindMapService { @Resource - private AiApiKeyService apiKeyService; - @Resource - private AiChatModelService chatModalService; + private AiModelService modalService; @Resource private AiChatRoleService chatRoleService; @@ -65,17 +63,17 @@ public class AiMindMapServiceImpl implements AiMindMapService { AiChatRoleDO role = CollUtil.getFirst( chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName())); // 1.1 获取导图执行模型 - AiChatModelDO model = getModel(role); + AiModelDO model = getModel(role); // 1.2 获取角色设定消息 String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage()) ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage(); // 1.3 校验平台 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); - ChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); + ChatModel chatModel = modalService.getChatModel(model.getId()); // 2. 插入思维导图信息 - AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, - mindMap -> mindMap.setUserId(userId).setModel(model.getModel()).setPlatform(platform.getPlatform())); + AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId) + .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel())); mindMapMapper.insert(mindMapDO); // 3.1 构建 Prompt,并进行调用 @@ -103,7 +101,7 @@ public class AiMindMapServiceImpl implements AiMindMapService { } - private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) { + private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) { // 1. 构建 message 列表 List chatMessages = buildMessages(generateReqVO, systemMessage); // 2. 构建 options 对象 @@ -123,13 +121,13 @@ public class AiMindMapServiceImpl implements AiMindMapService { return chatMessages; } - private AiChatModelDO getModel(AiChatRoleDO role) { - AiChatModelDO model = null; + private AiModelDO getModel(AiChatRoleDO role) { + AiModelDO model = null; if (role != null && role.getModelId() != null) { - model = chatModalService.getChatModel(role.getModelId()); + model = modalService.getModel(role.getModelId()); } if (model == null) { - model = chatModalService.getRequiredDefaultChatModel(); + model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); } Assert.notNull(model, "[AI] 获取不到模型"); return model; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java index 29d9e3e07a..44da80041c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java @@ -1,16 +1,10 @@ package cn.iocoder.yudao.module.ai.service.model; -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; import jakarta.validation.Valid; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.image.ImageModel; -import org.springframework.ai.vectorstore.VectorStore; import java.util.List; @@ -74,50 +68,13 @@ public interface AiApiKeyService { */ List getApiKeyList(); - // ========== 与 spring-ai 集成 ========== - /** - * 获得 ChatModel 对象 - * - * @param id 编号 - * @return ChatModel 对象 - */ - ChatModel getChatModel(Long id); - - /** - * 获得 ImageModel 对象 - * - * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择 + * 获得默认的 API 密钥 * * @param platform 平台 - * @return ImageModel 对象 + * @param status 状态 + * @return API 密钥 */ - ImageModel getImageModel(AiPlatformEnum platform); - - /** - * 获得 MidjourneyApi 对象 - * - * TODO 可优化点:目前默认获取 Midjourney 对应的第一个开启的配置用于绘画;后续可以支持配置选择 - * - * @return MidjourneyApi 对象 - */ - MidjourneyApi getMidjourneyApi(); - - /** - * 获得 SunoApi 对象 - * - * TODO 可优化点:目前默认获取 Suno 对应的第一个开启的配置用于音乐;后续可以支持配置选择 - * - * @return SunoApi 对象 - */ - SunoApi getSunoApi(); - - /** - * 获得 VectorStore 对象 - * - * @param modelId 编号 - * @return VectorStore 对象 - */ - VectorStore getOrCreateVectorStoreByModelId(Long modelId); + AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status); } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index 38d6c95495..f0bac8a6d9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -1,31 +1,21 @@ package cn.iocoder.yudao.module.ai.service.model; -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; -import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; import jakarta.annotation.Resource; -import org.springframework.ai.chat.model.ChatModel; -import org.springframework.ai.embedding.EmbeddingModel; -import org.springframework.ai.image.ImageModel; -import org.springframework.ai.vectorstore.SimpleVectorStore; -import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS; /** * AI API 密钥 Service 实现类 @@ -39,14 +29,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @Resource private AiApiKeyMapper apiKeyMapper; - // TODO @芋艿:后续要不要改? - @Resource - @Lazy // 延迟加载,解决渲染依赖 - private AiChatModelService chatModelService; - - @Resource - private AiModelFactory modelFactory; - @Override public Long createApiKey(AiApiKeySaveReqVO createReqVO) { // 插入 @@ -105,57 +87,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { return apiKeyMapper.selectList(); } - // ========== 与 spring-ai 集成 ========== - @Override - public ChatModel getChatModel(Long id) { - AiApiKeyDO apiKey = validateApiKey(id); - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); - return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl()); - } - - @Override - public ImageModel getImageModel(AiPlatformEnum platform) { - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); + public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) { + AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status); if (apiKey == null) { - throw exception(API_KEY_IMAGE_NODE_FOUND, platform.getName()); + throw exception(API_KEY_NOT_EXISTS); } - return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl()); - } - - @Override - public MidjourneyApi getMidjourneyApi() { - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus( - AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); - if (apiKey == null) { - throw exception(API_KEY_MIDJOURNEY_NOT_FOUND); - } - return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl()); - } - - @Override - public SunoApi getSunoApi() { - AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus( - AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); - if (apiKey == null) { - throw exception(API_KEY_SUNO_NOT_FOUND); - } - return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); - } - - @Override - public VectorStore getOrCreateVectorStoreByModelId(Long modelId) { - // 获取模型 + 密钥 - AiChatModelDO chatModel = chatModelService.validateChatModel(modelId); - AiApiKeyDO apiKey = validateApiKey(chatModel.getKeyId()); - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); - - // 创建或获取 EmbeddingModel 对象 - EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), - apiKey.getUrl(), chatModel.getModel()); - - // 创建或获取 VectorStore 对象 - return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); + return apiKey; } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java deleted file mode 100644 index f83ac73c91..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.model; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import jakarta.validation.Valid; - -import java.util.Collection; -import java.util.List; - -import java.util.Set; - -/** - * AI 聊天模型 Service 接口 - * - * @author fansili - * @since 2024/4/24 19:42 - */ -public interface AiChatModelService { - - /** - * 创建聊天模型 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO); - - /** - * 更新聊天模型 - * - * @param updateReqVO 更新信息 - */ - void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO); - - /** - * 删除聊天模型 - * - * @param id 编号 - */ - void deleteChatModel(Long id); - - /** - * 获得聊天模型 - * - * @param id 编号 - * @return 聊天模型 - */ - AiChatModelDO getChatModel(Long id); - - /** - * 获得默认的聊天模型 - * - * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常 - * - * @return 聊天模型 - */ - AiChatModelDO getRequiredDefaultChatModel(); - - /** - * 获得聊天模型分页 - * - * @param pageReqVO 分页查询 - * @return 聊天模型分页 - */ - PageResult getChatModelPage(AiChatModelPageReqVO pageReqVO); - - /** - * 校验聊天模型 - * - * @param id 编号 - * @return 聊天模型 - */ - AiChatModelDO validateChatModel(Long id); - - /** - * 获得聊天模型列表 - * - * @param status 状态 - * @return 聊天模型列表 - */ - List getChatModelListByStatus(Integer status); - - /** - * 获得聊天模型列表 - * - * @param ids 编号数组 - * @return 模型列表 - */ - List getChatModelList(Collection ids); -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java index 4b11602f54..9140882a1f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java @@ -1,114 +1,168 @@ package cn.iocoder.yudao.module.ai.service.model; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; +import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatModelMapper; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper; import jakarta.annotation.Resource; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.embedding.EmbeddingModel; +import org.springframework.ai.image.ImageModel; +import org.springframework.ai.vectorstore.SimpleVectorStore; +import org.springframework.ai.vectorstore.VectorStore; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.util.Collection; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** - * AI 聊天模型 Service 实现类 + * AI 模型 Service 实现类 * * @author fansili */ @Service @Validated -public class AiChatModelServiceImpl implements AiChatModelService { +public class AiChatModelServiceImpl implements AiModelService { @Resource private AiApiKeyService apiKeyService; @Resource - private AiChatModelMapper chatModelMapper; + private AiChatMapper modelMapper; + + @Resource + private AiModelFactory modelFactory; @Override - public Long createChatModel(AiChatModelSaveReqVO createReqVO) { + public Long createModel(AiModelSaveReqVO createReqVO) { // 1. 校验 AiPlatformEnum.validatePlatform(createReqVO.getPlatform()); apiKeyService.validateApiKey(createReqVO.getKeyId()); // 2. 插入 - AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class); - chatModelMapper.insert(chatModel); - return chatModel.getId(); + AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class); + modelMapper.insert(model); + return model.getId(); } @Override - public void updateChatModel(AiChatModelSaveReqVO updateReqVO) { + public void updateModel(AiModelSaveReqVO updateReqVO) { // 1. 校验 - validateChatModelExists(updateReqVO.getId()); + validateModelExists(updateReqVO.getId()); AiPlatformEnum.validatePlatform(updateReqVO.getPlatform()); apiKeyService.validateApiKey(updateReqVO.getKeyId()); // 2. 更新 - AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class); - chatModelMapper.updateById(updateObj); + AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class); + modelMapper.updateById(updateObj); } @Override - public void deleteChatModel(Long id) { + public void deleteModel(Long id) { // 校验存在 - validateChatModelExists(id); + validateModelExists(id); // 删除 - chatModelMapper.deleteById(id); + modelMapper.deleteById(id); } - private AiChatModelDO validateChatModelExists(Long id) { - AiChatModelDO model = chatModelMapper.selectById(id); - if (chatModelMapper.selectById(id) == null) { - throw exception(CHAT_MODEL_NOT_EXISTS); + private AiModelDO validateModelExists(Long id) { + AiModelDO model = modelMapper.selectById(id); + if (modelMapper.selectById(id) == null) { + throw exception(MODEL_NOT_EXISTS); } return model; } @Override - public AiChatModelDO getChatModel(Long id) { - return chatModelMapper.selectById(id); + public AiModelDO getModel(Long id) { + return modelMapper.selectById(id); } @Override - public AiChatModelDO getRequiredDefaultChatModel() { - AiChatModelDO model = chatModelMapper.selectFirstByStatus(CommonStatusEnum.ENABLE.getStatus()); + public AiModelDO getRequiredDefaultModel(Integer type) { + AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus()); if (model == null) { - throw exception(CHAT_MODEL_DEFAULT_NOT_EXISTS); + throw exception(MODEL_DEFAULT_NOT_EXISTS); } return model; } @Override - public PageResult getChatModelPage(AiChatModelPageReqVO pageReqVO) { - return chatModelMapper.selectPage(pageReqVO); + public PageResult getModelPage(AiModelPageReqVO pageReqVO) { + return modelMapper.selectPage(pageReqVO); } @Override - public AiChatModelDO validateChatModel(Long id) { - AiChatModelDO model = validateChatModelExists(id); + public AiModelDO validateModel(Long id) { + AiModelDO model = validateModelExists(id); if (CommonStatusEnum.isDisable(model.getStatus())) { - throw exception(CHAT_MODEL_DISABLE); + throw exception(MODEL_DISABLE); } return model; } @Override - public List getChatModelListByStatus(Integer status) { - return chatModelMapper.selectList(status); + public List getModelListByStatusAndType(Integer status, Integer type, + String platform) { + return modelMapper.selectListByStatusAndType(status, type, platform); + } + + // ========== 与 Spring AI 集成 ========== + + @Override + public ChatModel getChatModel(Long id) { + AiModelDO model = validateModel(id); + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl()); } @Override - public List getChatModelList(Collection ids) { - return chatModelMapper.selectBatchIds(ids); + public ImageModel getImageModel(Long id) { + AiModelDO model = validateModel(id); + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl()); + } + + @Override + public MidjourneyApi getMidjourneyApi() { + AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey( + AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); + return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl()); + } + + @Override + public SunoApi getSunoApi() { + AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey( + AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); + return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl()); + } + + @Override + public VectorStore getOrCreateVectorStore(Long id) { + // 获取模型 + 密钥 + AiModelDO model = validateModel(id); + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); + + // 创建或获取 EmbeddingModel 对象 + EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel( + platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); + + // 创建或获取 VectorStore 对象 + return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java new file mode 100644 index 0000000000..e3746b14d2 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java @@ -0,0 +1,131 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; +import jakarta.validation.Valid; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.image.ImageModel; +import org.springframework.ai.vectorstore.VectorStore; + +import javax.annotation.Nullable; +import java.util.List; + +/** + * AI 模型 Service 接口 + * + * @author fansili + * @since 2024/4/24 19:42 + */ +public interface AiModelService { + + /** + * 创建模型 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createModel(@Valid AiModelSaveReqVO createReqVO); + + /** + * 更新模型 + * + * @param updateReqVO 更新信息 + */ + void updateModel(@Valid AiModelSaveReqVO updateReqVO); + + /** + * 删除模型 + * + * @param id 编号 + */ + void deleteModel(Long id); + + /** + * 获得模型 + * + * @param id 编号 + * @return 模型 + */ + AiModelDO getModel(Long id); + + /** + * 获得默认的模型 + * + * 如果获取不到,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常 + * + * @return 模型 + */ + AiModelDO getRequiredDefaultModel(Integer type); + + /** + * 获得模型分页 + * + * @param pageReqVO 分页查询 + * @return 模型分页 + */ + PageResult getModelPage(AiModelPageReqVO pageReqVO); + + /** + * 校验模型是否可使用 + * + * @param id 编号 + * @return 模型 + */ + AiModelDO validateModel(Long id); + + /** + * 获得模型列表 + * + * @param status 状态 + * @param type 类型 + * @param platform 平台,允许空 + * @return 模型列表 + */ + List getModelListByStatusAndType(Integer status, Integer type, + @Nullable String platform); + + // ========== 与 Spring AI 集成 ========== + + /** + * 获得 ChatModel 对象 + * + * @param id 编号 + * @return ChatModel 对象 + */ + ChatModel getChatModel(Long id); + + /** + * 获得 ImageModel 对象 + * + * @param id 编号 + * @return ImageModel 对象 + */ + ImageModel getImageModel(Long id); + + /** + * 获得 MidjourneyApi 对象 + * + * @return MidjourneyApi 对象 + */ + MidjourneyApi getMidjourneyApi(); + + /** + * 获得 SunoApi 对象 + * + * @return SunoApi 对象 + */ + SunoApi getSunoApi(); + + /** + * 获得 VectorStore 对象 + * + * @param id 编号 + * @return VectorStore 对象 + */ + VectorStore getOrCreateVectorStore(Long id); + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java index 3f10ec8402..e4ff81a477 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java @@ -16,7 +16,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper; import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum; import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import cn.iocoder.yudao.module.infra.api.file.FileApi; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -41,7 +41,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXIS public class AiMusicServiceImpl implements AiMusicService { @Resource - private AiApiKeyService apiKeyService; + private AiModelService modelService; @Resource private AiMusicMapper musicMapper; @@ -53,7 +53,7 @@ public class AiMusicServiceImpl implements AiMusicService { @Transactional(rollbackFor = Exception.class) public List generateMusic(Long userId, AiSunoGenerateReqVO reqVO) { // 1. 调用 Suno 生成音乐 - SunoApi sunoApi = apiKeyService.getSunoApi(); + SunoApi sunoApi = modelService.getSunoApi(); List musicDataList; if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) { // 1.1 描述模式 @@ -88,7 +88,7 @@ public class AiMusicServiceImpl implements AiMusicService { log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", streamingTask.size()); // GET 请求,为避免参数过长,分批次处理 - SunoApi sunoApi = apiKeyService.getSunoApi(); + SunoApi sunoApi = modelService.getSunoApi(); CollUtil.split(streamingTask, 36).forEach(chunkList -> { Map taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId); List musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet())); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index f238bd3e30..ceae2fc9c9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.write; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.util.AiUtils; import cn.iocoder.yudao.framework.common.pojo.CommonResult; @@ -11,17 +12,16 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO; import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper; import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; import cn.iocoder.yudao.module.ai.enums.DictTypeConstants; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum; -import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; -import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; +import cn.iocoder.yudao.module.ai.service.model.AiModelService; import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -54,17 +54,15 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXIS public class AiWriteServiceImpl implements AiWriteService { @Resource - private AiApiKeyService apiKeyService; - @Resource - private AiChatModelService chatModalService; + private AiModelService chatModalService; @Resource private AiChatRoleService chatRoleService; @Resource - private DictDataApi dictDataApi; + private AiWriteMapper writeMapper; @Resource - private AiWriteMapper writeMapper; + private DictDataApi dictDataApi; @Override public Flux> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) { @@ -72,17 +70,17 @@ public class AiWriteServiceImpl implements AiWriteService { AiChatRoleDO writeRole = CollUtil.getFirst( chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName())); // 1.1 获取写作执行模型 - AiChatModelDO model = getModel(writeRole); + AiModelDO model = getModel(writeRole); // 1.2 获取角色设定消息 String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage()) ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage(); // 1.3 校验平台 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); - StreamingChatModel chatModel = apiKeyService.getChatModel(model.getKeyId()); + StreamingChatModel chatModel = chatModalService.getChatModel(model.getKeyId()); // 2. 插入写作信息 - AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, - write -> write.setUserId(userId).setPlatform(platform.getPlatform()).setModel(model.getModel())); + AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId) + .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel())); writeMapper.insert(writeDO); // 3.1 构建 Prompt,并进行调用 @@ -109,19 +107,19 @@ public class AiWriteServiceImpl implements AiWriteService { }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR))); } - private AiChatModelDO getModel(AiChatRoleDO writeRole) { - AiChatModelDO model = null; + private AiModelDO getModel(AiChatRoleDO writeRole) { + AiModelDO model = null; if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { - model = chatModalService.getChatModel(writeRole.getModelId()); + model = chatModalService.getModel(writeRole.getModelId()); } if (model == null) { - model = chatModalService.getRequiredDefaultChatModel(); + model = chatModalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); } Assert.notNull(model, "[AI] 获取不到模型"); return model; } - private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiChatModelDO model, String systemMessage) { + private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) { // 1. 构建 message 列表 List chatMessages = buildMessages(generateReqVO, systemMessage); // 2. 构建 options 对象 diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiModelTypeEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiModelTypeEnum.java new file mode 100644 index 0000000000..4f7a4e462d --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiModelTypeEnum.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.framework.ai.core.enums; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * AI 模型类型的枚举 + * + * @author 芋道源码 + */ +@Getter +@RequiredArgsConstructor +public enum AiModelTypeEnum implements ArrayValuable { + + CHAT(1, "对话"), + IMAGE(2, "图片"), + VOICE(3, "语音"), + VIDEO(4, "视频"), + EMBEDDING(5, "向量"), + RERANK(6, "重排序"); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型名 + */ + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java index 7b479d8f1a..a154db8c88 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.framework.ai.core.enums; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Arrays; + /** * AI 模型平台 * @@ -10,7 +13,7 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum AiPlatformEnum { +public enum AiPlatformEnum implements ArrayValuable { // ========== 国内平台 ========== @@ -44,6 +47,8 @@ public enum AiPlatformEnum { */ private final String name; + public static final String[] ARRAYS = Arrays.stream(values()).map(AiPlatformEnum::getPlatform).toArray(String[]::new); + public static AiPlatformEnum validatePlatform(String platform) { for (AiPlatformEnum platformEnum : AiPlatformEnum.values()) { if (platformEnum.getPlatform().equals(platform)) { @@ -53,4 +58,9 @@ public enum AiPlatformEnum { throw new IllegalArgumentException("非法平台: " + platform); } + @Override + public String[] array() { + return ARRAYS; + } + } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 1f9b1f21a2..ffc052c5d3 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -456,25 +456,4 @@ public class AiModelFactoryImpl implements AiModelFactory { return vectorStore; } - /** - * 创建向量存储文件 - * - * @param embeddingModel 嵌入模型 - * @return 向量存储文件 - */ - private File createVectorStoreFile(EmbeddingModel embeddingModel) { - // 获取简单类名 - String simpleClassName = embeddingModel.getClass().getSimpleName(); - // 获取用户主目录 - String userHome = FileUtil.getUserHomePath(); - // 创建vector_store目录 - File vectorStoreDir = new File(userHome, "vector_store"); - if (!vectorStoreDir.exists()) { - vectorStoreDir.mkdirs(); - } - - // 创建文件 - return new File(vectorStoreDir, "simple_" + simpleClassName + ".json"); - } - } From 1c9c9790cd0feec6154637c20fa1bcb20d692dfb Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Mar 2025 21:48:22 +0800 Subject: [PATCH 238/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E2=80=9C=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=E9=87=8D=E6=9E=84=E4=B8=BA=E2=80=9C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=EF=BC=8C=E6=94=AF=E6=8C=81=20type?= =?UTF-8?q?=20=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../midjourney/AiMidjourneyImagineReqVO.java | 6 +-- .../ai/service/image/AiImageServiceImpl.java | 45 ++++++++++--------- .../service/model/AiChatModelServiceImpl.java | 6 +-- .../ai/service/model/AiModelService.java | 3 +- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java index b90882639d..efb5906157 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java @@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO { @NotEmpty(message = "提示词不能为空!") private String prompt; - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney") - @NotEmpty(message = "模型不能为空") - private String model; // 参考 MidjourneyApi.ModelEnum + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "模型编号不能为空") + private Long modelId; @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "图片宽度不能为空") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java index 7204f4d5b2..60ca9ac996 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.codec.Base64; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; @@ -217,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService { @Override @Transactional(rollbackFor = Exception.class) - public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) { - MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(); - // 1. 保存数据库 - AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) + public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) { + // 1. 校验模型 + AiModelDO model = modelService.validateModel(drawReqVO.getModelId()); + Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "平台不匹配"); + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId()); + + // 2. 保存数据库 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) - .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName()); imageMapper.insert(image); - // 2. 调用 Midjourney Proxy 提交任务 - List base64Array = StrUtil.isBlank(reqVO.getReferImageUrl()) ? null : - Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(reqVO.getReferImageUrl())))); + // 3. 调用 Midjourney Proxy 提交任务 + List base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null : + Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl())))); MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest( - base64Array, reqVO.getPrompt(),null, - MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(), - reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel())); + base64Array, drawReqVO.getPrompt(),null, + MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(), + drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel())); MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest); - // 3. 情况一【失败】:抛出业务异常 + // 4.1 情况一【失败】:抛出业务异常 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) { String description = imagineResponse.description().contains("quota_not_enough") ? "账户余额不足" : imagineResponse.description(); throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); } - // 4. 情况二【成功】:更新 taskId 和参数 + // 4.2 情况二【成功】:更新 taskId 和参数 imageMapper.updateById(new AiImageDO().setId(image.getId()) - .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(reqVO))); + .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO))); return image.getId(); } @Override public Integer midjourneySync() { - MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(); // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image - List imageList = imageMapper.selectListByStatusAndPlatform( + List images = imageMapper.selectListByStatusAndPlatform( AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform()); - if (CollUtil.isEmpty(imageList)) { + if (CollUtil.isEmpty(images)) { return 0; } // 1.2 调用 Midjourney Proxy 获取任务进展 - List taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId)); + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId()); + List taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId)); Map taskMap = convertMap(taskList, MidjourneyApi.Notify::id); // 2. 逐个处理,更新进展 int count = 0; - for (AiImageDO image : imageList) { + for (AiImageDO image : images) { MidjourneyApi.Notify notify = taskMap.get(image.getTaskId()); if (notify == null) { log.error("[midjourneySync][image({}) 查询不到进展]", image); @@ -320,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService { @Override public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) { - MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(); // 1.1 检查 image AiImageDO image = validateImageExists(reqVO.getId()); if (ObjUtil.notEqual(userId, image.getUserId())) { throw exception(IMAGE_NOT_EXISTS); } + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId()); // 1.2 检查 customId MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(), buttonX -> buttonX.customId().equals(reqVO.getCustomId())); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java index 9140882a1f..2b38a68cf7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java @@ -137,9 +137,9 @@ public class AiChatModelServiceImpl implements AiModelService { } @Override - public MidjourneyApi getMidjourneyApi() { - AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey( - AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); + public MidjourneyApi getMidjourneyApi(Long id) { + AiModelDO model = validateModel(id); + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl()); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java index e3746b14d2..9e6185d450 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java @@ -109,9 +109,10 @@ public interface AiModelService { /** * 获得 MidjourneyApi 对象 * + * @param id 编号 * @return MidjourneyApi 对象 */ - MidjourneyApi getMidjourneyApi(); + MidjourneyApi getMidjourneyApi(Long id); /** * 获得 SunoApi 对象 From 7ab61b6d061801c5a24cd1d974ad382f174da1fe Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 3 Mar 2025 21:50:52 +0800 Subject: [PATCH 239/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20kafka=20=E6=95=B0=E6=8D=AE=E6=A1=A5?= =?UTF-8?q?=E6=A2=81=E6=B6=88=E6=81=AF=E7=AD=89=E5=BE=85=E7=9A=84=E6=9C=80?= =?UTF-8?q?=E5=A4=A7=E6=97=B6=E9=97=B4=E8=B0=83=E6=95=B4=E4=B8=BA=2010=20?= =?UTF-8?q?=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/action/databridge/IotKafkaMQDataBridgeExecute.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index b943eb31af..9c125960b4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -26,7 +26,7 @@ import java.util.concurrent.TimeUnit; public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute> { - private static final Duration SEND_TIMEOUT = Duration.ofMillis(10); + private static final Duration SEND_TIMEOUT = Duration.ofMillis(10000); // 10 秒超时时间 @Override public Integer getType() { From 433e91da8e0d9b683e7375a2668e8c4b01b5472f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 3 Mar 2025 21:55:49 +0800 Subject: [PATCH 240/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E2=80=9C=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=E9=87=8D=E6=9E=84=E4=B8=BA=E2=80=9C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=EF=BC=8C=E6=94=AF=E6=8C=81=20type?= =?UTF-8?q?=20=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/service/chat/AiChatConversationServiceImpl.java | 9 +++++---- .../ai/service/knowledge/AiKnowledgeServiceImpl.java | 6 +++--- ...ChatModelServiceImpl.java => AiModelServiceImpl.java} | 2 +- .../module/ai/service/write/AiWriteServiceImpl.java | 8 ++++---- .../yudao/framework/ai/image/OpenAiImageModelTests.java | 4 ---- 5 files changed, 13 insertions(+), 16 deletions(-) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/{AiChatModelServiceImpl.java => AiModelServiceImpl.java} (98%) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java index 792a8ae150..6483166a7f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java @@ -45,7 +45,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService private AiChatConversationMapper chatConversationMapper; @Resource - private AiModelService chatModalService; + private AiModelService modalService; @Resource private AiChatRoleService chatRoleService; @Resource @@ -56,8 +56,8 @@ public class AiChatConversationServiceImpl implements AiChatConversationService // 1.1 获得 AiChatRoleDO 聊天角色 AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null; // 1.2 获得 AiModelDO 聊天模型 - AiModelDO model = role != null && role.getModelId() != null ? chatModalService.validateModel(role.getModelId()) - : chatModalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); + AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId()) + : modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); Assert.notNull(model, "必须找到默认模型"); validateChatModel(model); @@ -89,7 +89,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService // 1.2 校验模型是否存在(修改模型的情况) AiModelDO model = null; if (updateReqVO.getModelId() != null) { - model = chatModalService.validateModel(updateReqVO.getModelId()); + model = modalService.validateModel(updateReqVO.getModelId()); } // 1.3 校验知识库是否存在 @@ -144,6 +144,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) { return; } + Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), "模型类型不正确:" + model); throw exception(CHAT_CONVERSATION_MODEL_ERROR); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 55822796ce..0bd4029850 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -28,12 +28,12 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { private AiKnowledgeMapper knowledgeMapper; @Resource - private AiModelService chatModelService; + private AiModelService modelService; @Override public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) { // 1. 校验模型配置 - AiModelDO model = chatModelService.validateModel(createReqVO.getEmbeddingModelId()); + AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId()); // 2. 插入知识库 AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class) @@ -47,7 +47,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { // 1.1 校验知识库存在 validateKnowledgeExists(updateReqVO.getId()); // 1.2 校验模型配置 - AiModelDO model = chatModelService.validateModel(updateReqVO.getEmbeddingModelId()); + AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId()); // 2. 更新知识库 AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java similarity index 98% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index 2b38a68cf7..8583984c94 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -33,7 +33,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; */ @Service @Validated -public class AiChatModelServiceImpl implements AiModelService { +public class AiModelServiceImpl implements AiModelService { @Resource private AiApiKeyService apiKeyService; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index ceae2fc9c9..4c38cd94d2 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -54,7 +54,7 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXIS public class AiWriteServiceImpl implements AiWriteService { @Resource - private AiModelService chatModalService; + private AiModelService modalService; @Resource private AiChatRoleService chatRoleService; @@ -76,7 +76,7 @@ public class AiWriteServiceImpl implements AiWriteService { ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage(); // 1.3 校验平台 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); - StreamingChatModel chatModel = chatModalService.getChatModel(model.getKeyId()); + StreamingChatModel chatModel = modalService.getChatModel(model.getKeyId()); // 2. 插入写作信息 AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId) @@ -110,10 +110,10 @@ public class AiWriteServiceImpl implements AiWriteService { private AiModelDO getModel(AiChatRoleDO writeRole) { AiModelDO model = null; if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) { - model = chatModalService.getModel(writeRole.getModelId()); + model = modalService.getModel(writeRole.getModelId()); } if (model == null) { - model = chatModalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); + model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); } Assert.notNull(model, "[AI] 获取不到模型"); return model; diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/OpenAiImageModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/OpenAiImageModelTests.java index f7a64e6a6a..64e921a5ac 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/OpenAiImageModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/OpenAiImageModelTests.java @@ -5,13 +5,9 @@ import org.junit.jupiter.api.Test; import org.springframework.ai.image.ImageOptions; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; -import org.springframework.ai.openai.OpenAiChatModel; -import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.OpenAiImageOptions; -import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiImageApi; -import org.springframework.web.client.RestClient; /** * {@link OpenAiImageModel} 集成测试类 From 71b45a29a304e033ea29dafc546873caa50c6678 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Mar 2025 20:13:19 +0800 Subject: [PATCH 241/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AMQTT=20=E6=8F=92=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin/common/util/IotPluginCommonUtils.java | 4 ++-- .../emqx/upstream/IotDeviceUpstreamServer.java | 13 +++---------- .../upstream/router/IotDeviceAuthVertxHandler.java | 12 ++++++------ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java index 753e62c94b..d60df9cc0d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java @@ -50,10 +50,10 @@ public class IotPluginCommonUtils { } /** - * 将对象转换为JSON字符串后写入响应 + * 将对象转换为 JSON 字符串后写入响应 * * @param routingContext 路由上下文 - * @param data 要转换为JSON的数据对象 + * @param data 数据对象 */ @SuppressWarnings("deprecation") public static void writeJson(RoutingContext routingContext, Object data) { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index 54479df158..c9db423e99 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.emqx.upstream; +import cn.hutool.core.util.ArrayUtil; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.emqx.config.IotPluginEmqxProperties; import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceAuthVertxHandler; @@ -167,33 +168,28 @@ public class IotDeviceUpstreamServer { */ private Future subscribeToTopics() { String[] topics = emqxProperties.getMqttTopics(); - if (topics == null || topics.length == 0) { + if (ArrayUtil.isEmpty(topics)) { log.warn("[subscribeToTopics] 未配置MQTT主题,跳过订阅"); return Future.succeededFuture(); } - log.info("[subscribeToTopics] 开始订阅设备上行消息主题"); Future compositeFuture = Future.succeededFuture(); - for (String topic : topics) { String trimmedTopic = topic.trim(); if (trimmedTopic.isEmpty()) { continue; } - compositeFuture = compositeFuture.compose(v -> client.subscribe(trimmedTopic, DEFAULT_QOS.value()) .map(ack -> { log.info("[subscribeToTopics] 成功订阅主题: {}", trimmedTopic); return null; }) .recover(err -> { - log.error("[subscribeToTopics] 订阅主题失败: {}, 原因: {}", - trimmedTopic, err.getMessage()); + log.error("[subscribeToTopics] 订阅主题失败: {}, 原因: {}", trimmedTopic, err.getMessage()); return Future.succeededFuture(); // 继续订阅其他主题 })); } - return compositeFuture; } @@ -205,7 +201,6 @@ public class IotDeviceUpstreamServer { log.warn("[stop] 服务未运行,无需停止"); return; } - log.info("[stop] 开始关闭服务"); isRunning = false; @@ -213,11 +208,9 @@ public class IotDeviceUpstreamServer { CompletableFuture serverFuture = server != null ? server.close().toCompletionStage().toCompletableFuture() : CompletableFuture.completedFuture(null); - CompletableFuture clientFuture = client != null ? client.disconnect().toCompletionStage().toCompletableFuture() : CompletableFuture.completedFuture(null); - CompletableFuture vertxFuture = vertx != null ? vertx.close().toCompletionStage().toCompletableFuture() : CompletableFuture.completedFuture(null); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java index 350de674cd..3f3cf94e9a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -14,8 +14,7 @@ import java.util.Collections; /** * IoT Emqx 连接认证的 Vert.x Handler - * ... + * MQTT HTTP * * @author haohao */ @@ -31,28 +30,29 @@ public class IotDeviceAuthVertxHandler implements Handler { @SuppressWarnings("unchecked") public void handle(RoutingContext routingContext) { try { + // 构建认证请求 DTO JsonObject json = routingContext.body().asJsonObject(); String clientId = json.getString("clientid"); String username = json.getString("username"); String password = json.getString("password"); - - // 构建认证请求DTO IotDeviceEmqxAuthReqDTO authReqDTO = new IotDeviceEmqxAuthReqDTO() .setClientId(clientId) .setUsername(username) .setPassword(password); - // 调用认证API + // 调用认证 API CommonResult authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); if (authResult.getCode() != 0 || !authResult.getData()) { IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "deny")); return; } + // 响应结果 IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "allow")); } catch (Exception e) { - log.error("[handle][EMQX认证异常]", e); + log.error("[handle][EMQX 认证异常]", e); IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "deny")); } } + } \ No newline at end of file From cc24eca470cea786c065b327ef2e4ea1dbe06e40 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Mar 2025 20:19:30 +0800 Subject: [PATCH 242/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E3=80=91AI=EF=BC=9A=E2=80=9C=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=E9=87=8D=E6=9E=84=E4=B8=BA=E2=80=9C?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E2=80=9D=EF=BC=8C=E6=94=AF=E6=8C=81=20type?= =?UTF-8?q?=20=E6=A8=A1=E5=9E=8B=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 1 + .../segment/AiKnowledgeSegmentSaveReqVO.java | 21 +++++++++++++++++++ .../service/mindmap/AiMindMapServiceImpl.java | 12 ++++++++--- .../ai/service/write/AiWriteServiceImpl.java | 12 ++++++++--- 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 205fc80b10..9530d5bf57 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -17,6 +17,7 @@ public interface ErrorCodeConstants { ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!"); ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!"); ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型"); + ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "操作失败,该模型的模型类型不正确"); // ========== API 聊天角色 1-040-002-000 ========== ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java new file mode 100644 index 0000000000..0c5dad11db --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - AI 新增/修改知识库段落 request VO") +@Data +public class AiKnowledgeSegmentSaveReqVO { + + @Schema(description = "编号", example = "24790") + private Long id; + + @Schema(description = "知识库文档编号", example = "1024") + private Long documentId; + + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") + @NotEmpty(message = "切片内容不能为空") + private String content; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java index 3eb49441b5..0dc851c216 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.ai.service.mindmap; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; @@ -38,7 +38,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MIND_MAP_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** * AI 思维导图 Service 实现类 @@ -129,7 +129,13 @@ public class AiMindMapServiceImpl implements AiMindMapService { if (model == null) { model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); } - Assert.notNull(model, "[AI] 获取不到模型"); + // 校验模型存在、且合法 + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) { + throw exception(MODEL_USE_TYPE_ERROR); + } return model; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java index 4c38cd94d2..787f8d2046 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.ai.service.write; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.core.enums.AiModelTypeEnum; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; @@ -42,7 +42,7 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WRITE_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; /** * AI 写作 Service 实现类 @@ -115,7 +115,13 @@ public class AiWriteServiceImpl implements AiWriteService { if (model == null) { model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()); } - Assert.notNull(model, "[AI] 获取不到模型"); + // 校验模型存在、且合法 + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) { + throw exception(MODEL_USE_TYPE_ERROR); + } return model; } From 6ccd0ca61e6a3540c01dd61d7f7dede71e849332 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Mar 2025 23:12:24 +0800 Subject: [PATCH 243/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91AI=EF=BC=9A=E4=BC=98=E5=8C=96=20pom=20?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E7=9A=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/AiChatMessageServiceImpl.java | 1 - .../yudao-spring-boot-starter-ai/pom.xml | 102 +++++++++--------- .../src/main/resources/application.yaml | 6 -- 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 2298d9b1d9..51bcc45d44 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -120,7 +120,6 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext()); - // 3.2 召回段落 List segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId()); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index 156d3f0769..2d1a0af3aa 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -20,10 +20,11 @@ - ${spring-ai.groupId} - spring-ai-zhipuai-spring-boot-starter - ${spring-ai.version} + cn.iocoder.boot + yudao-common + + ${spring-ai.groupId} spring-ai-openai-spring-boot-starter @@ -44,16 +45,53 @@ spring-ai-stability-ai-spring-boot-starter ${spring-ai.version} - - - - - - - - - + + com.alibaba.cloud.ai + spring-ai-alibaba-starter + 1.0.0-M5.1 + + + + ${spring-ai.groupId} + spring-ai-qianfan-spring-boot-starter + ${spring-ai.version} + + + + ${spring-ai.groupId} + spring-ai-zhipuai-spring-boot-starter + ${spring-ai.version} + + + + + + ${spring-ai.groupId} + spring-ai-qdrant-store + ${spring-ai.version} + + + + + ${spring-ai.groupId} + spring-ai-redis-store + ${spring-ai.version} + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + + org.springframework.ai + spring-ai-milvus-store + ${spring-ai.version} + + + + ${spring-ai.groupId} spring-ai-tika-document-reader ${spring-ai.version} @@ -69,46 +107,6 @@ - - ${spring-ai.groupId} - spring-ai-redis-store - ${spring-ai.version} - - - - cn.iocoder.boot - yudao-spring-boot-starter-redis - - - - cn.iocoder.boot - yudao-common - - - - ${spring-ai.groupId} - spring-ai-qianfan-spring-boot-starter - ${spring-ai.version} - - - com.alibaba.cloud.ai - spring-ai-alibaba-starter - 1.0.0-M5.1 - - - - - - com.alibaba - dashscope-sdk-java - 2.14.0 - - - org.slf4j - slf4j-simple - - - diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index b3792c4c16..850b40906c 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -151,12 +151,6 @@ spring: redis: index: default-index prefix: "default:" - embedding: - transformer: - onnx: - model-uri: https://raw.gitcode.com/yudaocode/yudao-demo/raw/master/yudao-static/ai/model.onnx - tokenizer: - uri: https://raw.gitcode.com/yudaocode/yudao-demo/raw/master/yudao-static/ai/tokenizer.json qianfan: # 文心一言 api-key: x0cuLZ7XsaTCU08vuJWO87Lg secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK From 44bcc9476d7a3a1cdbebfab206640b2dac470c48 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Mar 2025 22:22:22 +0800 Subject: [PATCH 244/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E5=A2=9E=E5=8A=A0=20QdrantVectorS?= =?UTF-8?q?tore=20=E5=90=91=E9=87=8F=E5=BA=93=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeDocumentServiceImpl.java | 2 - .../AiKnowledgeSegmentServiceImpl.java | 11 ++-- .../ai/service/model/AiModelServiceImpl.java | 5 +- .../yudao-spring-boot-starter-ai/pom.xml | 1 + .../ai/config/YudaoAiAutoConfiguration.java | 5 +- .../ai/core/factory/AiModelFactoryImpl.java | 51 ++++++++++++++++++- .../src/main/resources/application-dev.yaml | 5 ++ .../src/main/resources/application-local.yaml | 4 +- .../src/main/resources/application.yaml | 6 +++ 9 files changed, 78 insertions(+), 12 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index b1513a9bd4..48dd78dbb9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -54,7 +54,6 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic private AiKnowledgeService knowledgeService; @Override - @Transactional(rollbackFor = Exception.class) public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { // 1. 校验参数 knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); @@ -74,7 +73,6 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic } @Override - @Transactional(rollbackFor = Exception.class) public List createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) { // 1. 校验参数 knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId()); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 910ff1249a..3b5ed91d55 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -115,6 +115,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService segmentMapper.updateById(newSegment); // 3.2 重新向量化,必须开启状态 if (CommonStatusEnum.isEnable(oldSegment.getStatus())) { + newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId()); writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent())); } } @@ -156,9 +157,10 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) { // 1. 向量存储 - segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId()); - segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId()); - segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId()); + // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore + segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString()); + segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString()); + segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString()); vectorStore.add(List.of(segment)); // 2. 更新向量 ID @@ -190,7 +192,8 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService .similarityThreshold( ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold())) .filterExpression(new FilterExpressionBuilder() - .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId()).build()) + .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString()) + .build()) .build()); if (CollUtil.isEmpty(documents)) { return ListUtil.empty(); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index 8583984c94..16338ca3dc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -16,8 +16,8 @@ import jakarta.annotation.Resource; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; -import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -162,7 +162,8 @@ public class AiModelServiceImpl implements AiModelService { platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); // 创建或获取 VectorStore 对象 - return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); +// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); + return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index 2d1a0af3aa..ffcf3941e9 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -70,6 +70,7 @@ ${spring-ai.groupId} spring-ai-qdrant-store ${spring-ai.version} + diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 73c2b8db9b..ccad732e53 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; @@ -29,7 +30,9 @@ import org.springframework.context.annotation.Lazy; * @author fansili */ @AutoConfiguration -@EnableConfigurationProperties(YudaoAiProperties.class) +@EnableConfigurationProperties({YudaoAiProperties.class, + QdrantVectorStoreProperties.class // 解析 Qdrant 配置 +}) @Slf4j public class YudaoAiAutoConfiguration { diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index ffc052c5d3..6b0a1c3187 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Singleton; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; @@ -26,6 +27,9 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; import com.azure.ai.openai.OpenAIClientBuilder; +import io.micrometer.observation.ObservationRegistry; +import io.qdrant.client.QdrantClient; +import io.qdrant.client.QdrantGrpcClient; import lombok.SneakyThrows; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; @@ -33,11 +37,14 @@ import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionPr import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.document.MetadataMode; +import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.ollama.OllamaChatModel; @@ -57,10 +64,15 @@ import org.springframework.ai.stabilityai.StabilityAiImageModel; import org.springframework.ai.stabilityai.api.StabilityAiApi; import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; +import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; +import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import org.springframework.ai.zhipuai.ZhiPuAiChatModel; import org.springframework.ai.zhipuai.ZhiPuAiImageModel; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.web.client.RestClient; import java.io.File; @@ -214,13 +226,14 @@ public class AiModelFactoryImpl implements AiModelFactory { @Override public VectorStore getOrCreateVectorStore(Class type, EmbeddingModel embeddingModel) { - // String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, - // url); String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); return Singleton.get(cacheKey, (Func0) () -> { if (type == SimpleVectorStore.class) { return buildSimpleVectorStore(embeddingModel); } + if (type == QdrantVectorStore.class) { + return buildQdrantVectorStore(embeddingModel); + } throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); // TODO @芋艿:先临时使用 store // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的 @@ -456,4 +469,38 @@ public class AiModelFactoryImpl implements AiModelFactory { return vectorStore; } + private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) { + QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration(); + QdrantVectorStoreProperties vectorStoreProperties = SpringUtil.getBean(QdrantVectorStoreProperties.class); + // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象 + QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder( + vectorStoreProperties.getHost(), vectorStoreProperties.getPort(), vectorStoreProperties.isUseTls()); + if (StrUtil.isNotEmpty(vectorStoreProperties.getApiKey())) { + grpcClientBuilder.withApiKey(vectorStoreProperties.getApiKey()); + } + QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build()); + // 参考 QdrantVectorStoreAutoConfiguration 实现,实现 batchingStrategy + BatchingStrategy batchingStrategy = ReflectUtil.invoke(configuration, "batchingStrategy"); + + // 创建 QdrantVectorStore 对象 + ObjectProvider observationRegistry = new ObjectProvider<>() { + + @Override + public ObservationRegistry getObject() throws BeansException { + return SpringUtil.getBean(ObservationRegistry.class); + } + + }; + ObjectProvider customObservationConvention = new ObjectProvider<>() { + + @Override + public VectorStoreObservationConvention getObject() throws BeansException { + return new DefaultVectorStoreObservationConvention(); + } + + }; + return configuration.vectorStore(embeddingModel, vectorStoreProperties, qdrantClient, + observationRegistry, customObservationConvention, batchingStrategy); + } + } diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index cd8652c79e..9377af95cd 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -4,6 +4,11 @@ server: --- #################### 数据库相关配置 #################### spring: + spring: + autoconfigure: + exclude: + - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 885f73db59..60564f094b 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -3,13 +3,15 @@ server: --- #################### 数据库相关配置 #################### spring: - # 数据源配置项 autoconfigure: exclude: - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 web-stat-filter: diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 850b40906c..08888ffb9d 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -151,6 +151,12 @@ spring: redis: index: default-index prefix: "default:" + qdrant: + collection-name: knowledge_segment + host: 127.0.0.1 + port: 6334 + use-tls: false + api-key: qianfan: # 文心一言 api-key: x0cuLZ7XsaTCU08vuJWO87Lg secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK From 824a801b39225d356bf5a7d6745e62d084b93a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Fri, 7 Mar 2025 22:36:38 +0800 Subject: [PATCH 245/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=B7=BB=E5=8A=A0=20Webhook=20?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=99=A8=E4=BB=A5=E5=A4=84=E7=90=86=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E8=BF=9E=E6=8E=A5=E5=92=8C=E6=96=AD=E5=BC=80=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=E8=AE=BE=E5=A4=87=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E7=AE=A1=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../upstream/IotDeviceUpstreamServer.java | 4 + .../router/IotDeviceWebhookVertxHandler.java | 154 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index c9db423e99..040985ba9a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.emqx.config.IotPluginEmqxProperties; import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceAuthVertxHandler; import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceMqttMessageHandler; +import cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router.IotDeviceWebhookVertxHandler; import io.netty.handler.codec.mqtt.MqttQoS; import io.vertx.core.Future; import io.vertx.core.Vertx; @@ -70,6 +71,9 @@ public class IotDeviceUpstreamServer { // TODO @haohao:疑问,mqtt 的认证,需要通过 http 呀? // 回复:MQTT 认证不必须通过 HTTP 进行,但 HTTP 认证是 EMQX 等 MQTT 服务器支持的一种灵活的认证方式 .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi)); + // 添加 Webhook 处理器,用于处理设备连接和断开连接事件 + router.post(IotDeviceWebhookVertxHandler.PATH) + .handler(new IotDeviceWebhookVertxHandler(deviceUpstreamApi)); // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); this.mqttMessageHandler = new IotDeviceMqttMessageHandler(deviceUpstreamApi, client); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java new file mode 100644 index 0000000000..a2499826fd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java @@ -0,0 +1,154 @@ +package cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.util.Collections; + +/** + * IoT Emqx Webhook 事件处理的 Vert.x Handler + * EMQX + * Webhook + * + * @author haohao + */ +@RequiredArgsConstructor +@Slf4j +public class IotDeviceWebhookVertxHandler implements Handler { + + public static final String PATH = "/mqtt/webhook"; + + private final IotDeviceUpstreamApi deviceUpstreamApi; + + @Override + public void handle(RoutingContext routingContext) { + try { + // 解析请求体 + JsonObject json = routingContext.body().asJsonObject(); + String event = json.getString("event"); + String clientId = json.getString("clientid"); + String username = json.getString("username"); + + // 处理不同的事件类型 + switch (event) { + case "client.connected": + handleClientConnected(clientId, username); + break; + case "client.disconnected": + handleClientDisconnected(clientId, username); + break; + default: + log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, + username); + break; + } + + // 返回成功响应 + IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "success")); + } catch (Exception e) { + log.error("[handle][处理 Webhook 事件异常]", e); + IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "error")); + } + } + + /** + * 处理客户端连接事件 + * + * @param clientId 客户端ID + * @param username 用户名 + */ + private void handleClientConnected(String clientId, String username) { + if (StrUtil.isEmpty(username) || "undefined".equals(username)) { + log.warn("[handleClientConnected][客户端连接事件,但用户名为空] clientId={}", clientId); + return; + } + + // 解析产品标识和设备名称 + String[] parts = parseUsername(username); + if (parts == null) { + return; + } + + // 更新设备状态为在线 + IotDeviceStateUpdateReqDTO updateReqDTO = new IotDeviceStateUpdateReqDTO(); + updateReqDTO.setProductKey(parts[1]); + updateReqDTO.setDeviceName(parts[0]); + updateReqDTO.setState(IotDeviceStateEnum.ONLINE.getState()); + updateReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); + updateReqDTO.setReportTime(LocalDateTime.now()); + + CommonResult result = deviceUpstreamApi.updateDeviceState(updateReqDTO); + if (result.getCode() != 0 || !result.getData()) { + log.error("[handleClientConnected][更新设备状态为在线失败] clientId={}, username={}, code={}, msg={}", + clientId, username, result.getCode(), result.getMsg()); + } else { + log.info("[handleClientConnected][更新设备状态为在线成功] clientId={}, username={}", clientId, username); + } + } + + /** + * 处理客户端断开连接事件 + * + * @param clientId 客户端ID + * @param username 用户名 + */ + private void handleClientDisconnected(String clientId, String username) { + if (StrUtil.isEmpty(username) || "undefined".equals(username)) { + log.warn("[handleClientDisconnected][客户端断开连接事件,但用户名为空] clientId={}", clientId); + return; + } + + // 解析产品标识和设备名称 + String[] parts = parseUsername(username); + if (parts == null) { + return; + } + + // 更新设备状态为离线 + IotDeviceStateUpdateReqDTO offlineReqDTO = new IotDeviceStateUpdateReqDTO(); + offlineReqDTO.setProductKey(parts[1]); + offlineReqDTO.setDeviceName(parts[0]); + offlineReqDTO.setState(IotDeviceStateEnum.OFFLINE.getState()); + offlineReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); + offlineReqDTO.setReportTime(LocalDateTime.now()); + + CommonResult offlineResult = deviceUpstreamApi.updateDeviceState(offlineReqDTO); + if (offlineResult.getCode() != 0 || !offlineResult.getData()) { + log.error("[handleClientDisconnected][更新设备状态为离线失败] clientId={}, username={}, code={}, msg={}", + clientId, username, offlineResult.getCode(), offlineResult.getMsg()); + } else { + log.info("[handleClientDisconnected][更新设备状态为离线成功] clientId={}, username={}", clientId, username); + } + } + + /** + * 解析用户名,格式为 deviceName&productKey + * + * @param username 用户名 + * @return 解析结果,[0]为deviceName,[1]为productKey,解析失败返回null + */ + private String[] parseUsername(String username) { + if (StrUtil.isEmpty(username)) { + return null; + } + + String[] parts = username.split("&"); + if (parts.length != 2) { + log.warn("[parseUsername][用户名格式不正确,无法解析产品标识和设备名称] username={}", username); + return null; + } + + return parts; + } +} \ No newline at end of file From 831970233c63cd50f056b330f0e675102d2dd9c3 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 8 Mar 2025 10:50:22 +0800 Subject: [PATCH 246/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E7=9A=84=E6=89=A7=E8=A1=8C=E5=99=A8=E6=A0=B9=E6=8D=AE=E5=BC=95?= =?UTF-8?q?=E5=85=A5=E7=9A=84=E6=B6=88=E6=81=AF=E9=98=9F=E5=88=97=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../databridge/IotKafkaMQDataBridgeExecute.java | 2 ++ .../databridge/IotRabbitMQDataBridgeExecute.java | 2 ++ .../databridge/IotRocketMQDataBridgeExecute.java | 2 ++ yudao-server/pom.xml | 13 +++++++++++++ 4 files changed, 19 insertions(+) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 9c125960b4..6bcf41ab68 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Component; @@ -21,6 +22,7 @@ import java.util.concurrent.TimeUnit; * * @author HUIHUI */ +@ConditionalOnClass(KafkaTemplate.class) @Component @Slf4j public class IotKafkaMQDataBridgeExecute extends diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index f99e651795..ebe9d8ddd0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -7,6 +7,7 @@ import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; @@ -17,6 +18,7 @@ import java.time.LocalDateTime; * * @author HUIHUI */ +@ConditionalOnClass(Channel.class) @Component @Slf4j public class IotRabbitMQDataBridgeExecute extends diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index df413c7503..655ad55ebe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -9,6 +9,7 @@ import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendStatus; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @@ -18,6 +19,7 @@ import java.time.LocalDateTime; * * @author HUIHUI */ +@ConditionalOnClass(DefaultMQProducer.class) @Component @Slf4j public class IotRocketMQDataBridgeExecute extends diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 492e31db56..2cf04e8eb9 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -114,6 +114,19 @@ yudao-module-iot-biz ${revision} + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + + org.springframework.kafka + spring-kafka + + + org.springframework.boot + spring-boot-starter-amqp + From 415dd435f36d995309f2360289602b2d062311b5 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 8 Mar 2025 11:25:03 +0800 Subject: [PATCH 247/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E7=9A=84=E6=89=A7=E8=A1=8C=E5=99=A8=E6=A0=B9=E6=8D=AE=E5=BC=95?= =?UTF-8?q?=E5=85=A5=E7=9A=84=E6=B6=88=E6=81=AF=E9=98=9F=E5=88=97=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/action/databridge/IotKafkaMQDataBridgeExecute.java | 2 +- .../rule/action/databridge/IotRabbitMQDataBridgeExecute.java | 2 +- .../rule/action/databridge/IotRocketMQDataBridgeExecute.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 6bcf41ab68..6e3ef67a25 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit; * * @author HUIHUI */ -@ConditionalOnClass(KafkaTemplate.class) +@ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") @Component @Slf4j public class IotKafkaMQDataBridgeExecute extends diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index ebe9d8ddd0..5599144dad 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -18,7 +18,7 @@ import java.time.LocalDateTime; * * @author HUIHUI */ -@ConditionalOnClass(Channel.class) +@ConditionalOnClass(name = "com.rabbitmq.client.Channel") @Component @Slf4j public class IotRabbitMQDataBridgeExecute extends diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index 655ad55ebe..542d190ed5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -19,7 +19,7 @@ import java.time.LocalDateTime; * * @author HUIHUI */ -@ConditionalOnClass(DefaultMQProducer.class) +@ConditionalOnClass(name = "org.apache.rocketmq.client.producer.DefaultMQProducer") @Component @Slf4j public class IotRocketMQDataBridgeExecute extends From e66c69932f873709fd85df62ccbbcf5662cf2cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 8 Mar 2025 21:59:54 +0800 Subject: [PATCH 248/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT:=20=E6=9B=B4=E6=96=B0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=98=A0=E5=B0=84=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AF=B9=20device=5Fkey=20=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index e506b49d4b..955b384780 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -34,7 +34,7 @@ public interface IotDevicePropertyMapper { List newFields) { // TODO @芋艿:需要处理 device_key,重新发布的时候 oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), - TDengineTableField.FIELD_TS, "report_time")); + TDengineTableField.FIELD_TS, "report_time", "device_key")); List addFields = newFields.stream().filter( // 新增的字段 newField -> oldFields.stream().noneMatch(oldField -> oldField.getField().equals(newField.getField()))) .collect(Collectors.toList()); From 588c9fe323e2f48f1c3a6fa7159755279049cc09 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 8 Mar 2025 22:09:16 +0800 Subject: [PATCH 249/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E5=A2=9E=E5=8A=A0=20RedisVectorSt?= =?UTF-8?q?ore=20=E5=90=91=E9=87=8F=E5=BA=93=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeSegmentServiceImpl.java | 14 ++- .../ai/service/model/AiModelService.java | 4 +- .../ai/service/model/AiModelServiceImpl.java | 8 +- .../ai/config/YudaoAiAutoConfiguration.java | 31 +----- .../ai/core/factory/AiModelFactory.java | 9 +- .../ai/core/factory/AiModelFactoryImpl.java | 100 ++++++++++++------ .../src/main/resources/application.yaml | 8 +- 7 files changed, 103 insertions(+), 71 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 3b5ed91d55..efdd86203f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -48,9 +49,14 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM @Slf4j public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { - public static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId"; - public static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId"; - public static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId"; + private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId"; + private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId"; + private static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId"; + + private static final Map> VECTOR_STORE_METADATA_TYPES = Map.of( + VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class, + VECTOR_STORE_METADATA_DOCUMENT_ID, String.class, + VECTOR_STORE_METADATA_SEGMENT_ID, String.class); @Resource private AiKnowledgeSegmentMapper segmentMapper; @@ -257,7 +263,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) { - return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId()); + return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES); } private VectorStore getVectorStoreById(Long knowledgeId) { diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java index 9e6185d450..127f72cc46 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java @@ -13,6 +13,7 @@ import org.springframework.ai.vectorstore.VectorStore; import javax.annotation.Nullable; import java.util.List; +import java.util.Map; /** * AI 模型 Service 接口 @@ -125,8 +126,9 @@ public interface AiModelService { * 获得 VectorStore 对象 * * @param id 编号 + * @param metadataFields 元数据的定义 * @return VectorStore 对象 */ - VectorStore getOrCreateVectorStore(Long id); + VectorStore getOrCreateVectorStore(Long id, Map> metadataFields); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index 16338ca3dc..d6649a4b36 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -22,6 +22,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; @@ -151,7 +152,7 @@ public class AiModelServiceImpl implements AiModelService { } @Override - public VectorStore getOrCreateVectorStore(Long id) { + public VectorStore getOrCreateVectorStore(Long id, Map> metadataFields) { // 获取模型 + 密钥 AiModelDO model = validateModel(id); AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); @@ -162,8 +163,9 @@ public class AiModelServiceImpl implements AiModelService { platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); // 创建或获取 VectorStore 对象 -// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); - return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel); +// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); + return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); +// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index ccad732e53..38e6eb2e33 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -12,12 +12,12 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; import org.springframework.ai.tokenizer.TokenCountEstimator; -import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -31,7 +31,8 @@ import org.springframework.context.annotation.Lazy; */ @AutoConfiguration @EnableConfigurationProperties({YudaoAiProperties.class, - QdrantVectorStoreProperties.class // 解析 Qdrant 配置 + QdrantVectorStoreProperties.class, // 解析 Qdrant 配置 + RedisVectorStoreProperties.class, // 解析 Redis 配置 }) @Slf4j public class YudaoAiAutoConfiguration { @@ -200,32 +201,6 @@ public class YudaoAiAutoConfiguration { // return new TransformersEmbeddingModel(MetadataMode.EMBED); // } - /** - * TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel - */ -// @Bean -// @Lazy // TODO 芋艿:临时注释,避免无法启动 -// public RedisVectorStore vectorStore(TransformersEmbeddingModel embeddingModel, RedisVectorStoreProperties properties, -// RedisProperties redisProperties) { -// var config = RedisVectorStore.RedisVectorStoreConfig.builder() -// .withIndexName(properties.getIndex()) -// .withPrefix(properties.getPrefix()) -// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC)) -// .build(); -// -// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel, -// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), -// properties.isInitializeSchema()); -// redisVectorStore.afterPropertiesSet(); -// return redisVectorStore; -// } - @Bean - @Lazy // TODO 芋艿:临时注释,避免无法启动 - public TokenTextSplitter tokenTextSplitter() { - //TODO @xin 配置提取 - return new TokenTextSplitter(500, 100, 5, 10000, true); - } - @Bean @Lazy // TODO 芋艿:临时注释,避免无法启动 public TokenCountEstimator tokenCountEstimator() { diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java index 3a8bf5e04d..66ab41def7 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java @@ -8,6 +8,8 @@ import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.vectorstore.VectorStore; +import java.util.Map; + /** * AI Model 模型工厂的接口类 * @@ -96,13 +98,16 @@ public interface AiModelFactory { /** * 基于指定配置,获得 VectorStore 对象 - *

+ * * 如果不存在,则进行创建 * * @param type 向量存储类型 * @param embeddingModel 向量模型 + * @param metadataFields 元数据字段 * @return VectorStore 对象 */ - VectorStore getOrCreateVectorStore(Class type, EmbeddingModel embeddingModel); + VectorStore getOrCreateVectorStore(Class type, + EmbeddingModel embeddingModel, + Map> metadataFields); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 6b0a1c3187..f7c77c45b8 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -19,6 +19,7 @@ import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; @@ -39,12 +40,13 @@ import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.document.MetadataMode; -import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.ollama.OllamaChatModel; @@ -67,20 +69,26 @@ import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; +import org.springframework.ai.vectorstore.redis.RedisVectorStore; import org.springframework.ai.zhipuai.ZhiPuAiChatModel; import org.springframework.ai.zhipuai.ZhiPuAiImageModel; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.web.client.RestClient; +import redis.clients.jedis.JedisPooled; import java.io.File; import java.time.Duration; import java.util.List; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + /** * AI Model 模型工厂的实现类 * @@ -225,7 +233,9 @@ public class AiModelFactoryImpl implements AiModelFactory { } @Override - public VectorStore getOrCreateVectorStore(Class type, EmbeddingModel embeddingModel) { + public VectorStore getOrCreateVectorStore(Class type, + EmbeddingModel embeddingModel, + Map> metadataFields) { String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); return Singleton.get(cacheKey, (Func0) () -> { if (type == SimpleVectorStore.class) { @@ -234,23 +244,10 @@ public class AiModelFactoryImpl implements AiModelFactory { if (type == QdrantVectorStore.class) { return buildQdrantVectorStore(embeddingModel); } + if (type == RedisVectorStore.class) { + return buildRedisVectorStore(embeddingModel, metadataFields); + } throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); - // TODO @芋艿:先临时使用 store - // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的 - // String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey); - // var config = RedisVectorStore.RedisVectorStoreConfig.builder() - // .withIndexName(cacheKey) - // .withPrefix(prefix) - // .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", - // Schema.FieldType.NUMERIC)) - // .build(); - // RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); - // RedisVectorStore redisVectorStore = new RedisVectorStore(config, - // embeddingModel, - // new JedisPooled(redisProperties.getHost(), redisProperties.getPort()), - // true); - // redisVectorStore.afterPropertiesSet(); - // return redisVectorStore; }); } @@ -469,21 +466,65 @@ public class AiModelFactoryImpl implements AiModelFactory { return vectorStore; } + /** + * 参考 {@link QdrantVectorStoreAutoConfiguration} 的 vectorStore 方法 + */ + @SneakyThrows private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) { QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration(); - QdrantVectorStoreProperties vectorStoreProperties = SpringUtil.getBean(QdrantVectorStoreProperties.class); + QdrantVectorStoreProperties properties = SpringUtil.getBean(QdrantVectorStoreProperties.class); // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象 QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder( - vectorStoreProperties.getHost(), vectorStoreProperties.getPort(), vectorStoreProperties.isUseTls()); - if (StrUtil.isNotEmpty(vectorStoreProperties.getApiKey())) { - grpcClientBuilder.withApiKey(vectorStoreProperties.getApiKey()); + properties.getHost(), properties.getPort(), properties.isUseTls()); + if (StrUtil.isNotEmpty(properties.getApiKey())) { + grpcClientBuilder.withApiKey(properties.getApiKey()); } QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build()); - // 参考 QdrantVectorStoreAutoConfiguration 实现,实现 batchingStrategy - BatchingStrategy batchingStrategy = ReflectUtil.invoke(configuration, "batchingStrategy"); - // 创建 QdrantVectorStore 对象 - ObjectProvider observationRegistry = new ObjectProvider<>() { + QdrantVectorStore vectorStore = configuration.vectorStore(embeddingModel, properties, qdrantClient, + getObservationRegistry(), getCustomObservationConvention(), + ReflectUtil.invoke(configuration, "batchingStrategy")); + // 初始化索引 + vectorStore.afterPropertiesSet(); + return vectorStore; + } + + /** + * 参考 {@link RedisVectorStoreAutoConfiguration} 的 vectorStore 方法 + */ + private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel, + Map> metadataFields) { + // 创建 JedisPooled 对象 + RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); + JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort()); + // 创建 RedisVectorStoreProperties 对象 + RedisVectorStoreAutoConfiguration configuration = new RedisVectorStoreAutoConfiguration(); + RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class); + RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel) + .indexName(properties.getIndex()).prefix(properties.getPrefix()) + .initializeSchema(properties.isInitializeSchema()) + .metadataFields(convertList(metadataFields.entrySet(), entry -> { + String fieldName = entry.getKey(); + Class fieldType = entry.getValue(); + if (Number.class.isAssignableFrom(fieldType)) { + return RedisVectorStore.MetadataField.numeric(fieldName); + } + if (Boolean.class.isAssignableFrom(fieldType)) { + return RedisVectorStore.MetadataField.tag(fieldName); + } + return RedisVectorStore.MetadataField.text(fieldName); + })) + .observationRegistry(getObservationRegistry().getObject()) + .customObservationConvention(getCustomObservationConvention().getObject()) + .batchingStrategy(ReflectUtil.invoke(configuration, "batchingStrategy")) + .build(); + // 初始化索引 + redisVectorStore.afterPropertiesSet(); + return redisVectorStore; + } + + private static ObjectProvider getObservationRegistry() { + return new ObjectProvider<>() { @Override public ObservationRegistry getObject() throws BeansException { @@ -491,16 +532,15 @@ public class AiModelFactoryImpl implements AiModelFactory { } }; - ObjectProvider customObservationConvention = new ObjectProvider<>() { + } + private static ObjectProvider getCustomObservationConvention() { + return new ObjectProvider<>() { @Override public VectorStoreObservationConvention getObject() throws BeansException { return new DefaultVectorStoreObservationConvention(); } - }; - return configuration.vectorStore(embeddingModel, vectorStoreProperties, qdrantClient, - observationRegistry, customObservationConvention, batchingStrategy); } } diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 08888ffb9d..4120a73467 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -149,10 +149,12 @@ spring: ai: vectorstore: # 向量存储 redis: - index: default-index - prefix: "default:" + initialize-schema: true + index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行 + prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构 qdrant: - collection-name: knowledge_segment + initialize-schema: true + collection-name: knowledge_segment # Qdrant 中向量集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行 host: 127.0.0.1 port: 6334 use-tls: false From f2ee2008e6d84bf9d30113592a239803ff0fa5d5 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 09:27:33 +0800 Subject: [PATCH 250/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E5=A2=9E=E5=8A=A0=20MilvusVectorS?= =?UTF-8?q?tore=20=E5=90=91=E9=87=8F=E5=BA=93=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/service/model/AiModelServiceImpl.java | 12 ++-- .../yudao-spring-boot-starter-ai/pom.xml | 1 - .../ai/config/YudaoAiAutoConfiguration.java | 20 ++++--- .../ai/core/factory/AiModelFactoryImpl.java | 55 +++++++++++++++++-- .../src/main/resources/application.yaml | 9 ++- 5 files changed, 75 insertions(+), 22 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index d6649a4b36..b73567a1c5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -17,7 +17,7 @@ import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; +import org.springframework.ai.vectorstore.milvus.MilvusVectorStore; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -114,8 +114,7 @@ public class AiModelServiceImpl implements AiModelService { } @Override - public List getModelListByStatusAndType(Integer status, Integer type, - String platform) { + public List getModelListByStatusAndType(Integer status, Integer type, String platform) { return modelMapper.selectListByStatusAndType(status, type, platform); } @@ -163,9 +162,10 @@ public class AiModelServiceImpl implements AiModelService { platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); // 创建或获取 VectorStore 对象 -// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); - return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); -// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); + // return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); + // return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); + // return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); + return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index ffcf3941e9..2d1a0af3aa 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -70,7 +70,6 @@ ${spring-ai.groupId} spring-ai-qdrant-store ${spring-ai.version} - diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 38e6eb2e33..bea8e5bc0d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -11,8 +11,12 @@ import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; +import org.springframework.ai.embedding.BatchingStrategy; +import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; @@ -22,7 +26,6 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Lazy; /** * 芋道 AI 自动配置 @@ -33,6 +36,7 @@ import org.springframework.context.annotation.Lazy; @EnableConfigurationProperties({YudaoAiProperties.class, QdrantVectorStoreProperties.class, // 解析 Qdrant 配置 RedisVectorStoreProperties.class, // 解析 Redis 配置 + MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置 }) @Slf4j public class YudaoAiAutoConfiguration { @@ -193,18 +197,16 @@ public class YudaoAiAutoConfiguration { return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl()); } - // ========== rag 相关 ========== - // TODO @xin 免费版本 -// @Bean -// @Lazy // TODO 芋艿:临时注释,避免无法启动」 -// public TransformersEmbeddingModel transformersEmbeddingClient() { -// return new TransformersEmbeddingModel(MetadataMode.EMBED); -// } + // ========== RAG 相关 ========== @Bean - @Lazy // TODO 芋艿:临时注释,避免无法启动 public TokenCountEstimator tokenCountEstimator() { return new JTokkitTokenCountEstimator(); } + @Bean + public BatchingStrategy batchingStrategy() { + return new TokenCountBatchingStrategy(); + } + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index f7c77c45b8..7953157009 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -5,7 +5,6 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Singleton; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.RuntimeUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; @@ -29,6 +28,7 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; import com.azure.ai.openai.OpenAIClientBuilder; import io.micrometer.observation.ObservationRegistry; +import io.milvus.client.MilvusServiceClient; import io.qdrant.client.QdrantClient; import io.qdrant.client.QdrantGrpcClient; import lombok.SneakyThrows; @@ -38,6 +38,10 @@ import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionPr import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientConnectionDetails; +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration; @@ -47,6 +51,7 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.document.MetadataMode; +import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.ollama.OllamaChatModel; @@ -66,6 +71,7 @@ import org.springframework.ai.stabilityai.StabilityAiImageModel; import org.springframework.ai.stabilityai.api.StabilityAiApi; import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.ai.vectorstore.milvus.MilvusVectorStore; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; @@ -247,6 +253,9 @@ public class AiModelFactoryImpl implements AiModelFactory { if (type == RedisVectorStore.class) { return buildRedisVectorStore(embeddingModel, metadataFields); } + if (type == MilvusVectorStore.class) { + return buildMilvusVectorStore(embeddingModel); + } throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); }); } @@ -482,8 +491,7 @@ public class AiModelFactoryImpl implements AiModelFactory { QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build()); // 创建 QdrantVectorStore 对象 QdrantVectorStore vectorStore = configuration.vectorStore(embeddingModel, properties, qdrantClient, - getObservationRegistry(), getCustomObservationConvention(), - ReflectUtil.invoke(configuration, "batchingStrategy")); + getObservationRegistry(), getCustomObservationConvention(), getBatchingStrategy()); // 初始化索引 vectorStore.afterPropertiesSet(); return vectorStore; @@ -516,13 +524,48 @@ public class AiModelFactoryImpl implements AiModelFactory { })) .observationRegistry(getObservationRegistry().getObject()) .customObservationConvention(getCustomObservationConvention().getObject()) - .batchingStrategy(ReflectUtil.invoke(configuration, "batchingStrategy")) + .batchingStrategy(getBatchingStrategy()) .build(); // 初始化索引 redisVectorStore.afterPropertiesSet(); return redisVectorStore; } + /** + * 参考 {@link MilvusVectorStoreAutoConfiguration} 的 vectorStore 方法 + */ + @SneakyThrows + private MilvusVectorStore buildMilvusVectorStore(EmbeddingModel embeddingModel) { + MilvusVectorStoreAutoConfiguration configuration = new MilvusVectorStoreAutoConfiguration(); + // 获取配置属性 + MilvusVectorStoreProperties serverProperties = SpringUtil.getBean(MilvusVectorStoreProperties.class); + MilvusServiceClientProperties clientProperties = SpringUtil.getBean(MilvusServiceClientProperties.class); + + // 创建 MilvusServiceClient 对象 + MilvusServiceClient milvusClient = configuration.milvusClient(serverProperties, clientProperties, + new MilvusServiceClientConnectionDetails() { + + @Override + public String getHost() { + return clientProperties.getHost(); + } + + @Override + public int getPort() { + return clientProperties.getPort(); + } + + } + ); + // 创建 MilvusVectorStore 对象 + MilvusVectorStore vectorStore = configuration.vectorStore(milvusClient, embeddingModel, serverProperties, + getBatchingStrategy(), getObservationRegistry(), getCustomObservationConvention()); + + // 初始化索引 + vectorStore.afterPropertiesSet(); + return vectorStore; + } + private static ObjectProvider getObservationRegistry() { return new ObjectProvider<>() { @@ -543,4 +586,8 @@ public class AiModelFactoryImpl implements AiModelFactory { }; } + private static BatchingStrategy getBatchingStrategy() { + return SpringUtil.getBean(BatchingStrategy.class); + } + } diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 4120a73467..419fab471b 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -157,8 +157,13 @@ spring: collection-name: knowledge_segment # Qdrant 中向量集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行 host: 127.0.0.1 port: 6334 - use-tls: false - api-key: + milvus: + initialize-schema: true + database-name: default # Milvus 中数据库的名称 + collection-name: knowledge_segment # Milvus 中集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行 + client: + host: 127.0.0.1 + port: 19530 qianfan: # 文心一言 api-key: x0cuLZ7XsaTCU08vuJWO87Lg secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK From f286a8139205815665ecc1c3090ae79e368360ee Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 09:43:12 +0800 Subject: [PATCH 251/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91AI=EF=BC=9A=E5=8D=87=E7=BA=A7=20tika=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E8=A7=A3=E5=86=B3=20pdf=20=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 2 +- .../java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index d6c0a642fc..54252350d6 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -60,7 +60,7 @@ 2.14.5 3.11.1 0.1.55 - 2.9.2 + 3.1.0 2.7.0 3.0.6 4.1.116.Final diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index b1a3a3b507..a96d305492 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -19,7 +19,7 @@ import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; public class AiUtils { public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { - //noinspection EnhancedSwitchMigration + // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: // TODO @芋艿:tongyi 暂时没 maxTokens 选项 From 9126691ac0f699c8f4971b2b04f62ba539b2a962 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 11:51:19 +0800 Subject: [PATCH 252/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E8=81=8A=E5=A4=A9=E8=A7=92?= =?UTF-8?q?=E8=89=B2=EF=BC=8C=E6=96=B0=E5=A2=9E=20document=20=E7=9A=84?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AiKnowledgeDocumentController.java | 13 +++++++ .../admin/model/AiChatRoleController.java | 2 +- .../model/vo/chatRole/AiChatRoleRespVO.java | 6 ++- .../vo/chatRole/AiChatRoleSaveMyReqVO.java | 5 +++ .../vo/chatRole/AiChatRoleSaveReqVO.java | 5 +++ .../ai/dal/dataobject/model/AiChatRoleDO.java | 13 +++++++ .../knowledge/AiKnowledgeDocumentMapper.java | 7 +++- .../knowledge/AiKnowledgeDocumentService.java | 8 ++++ .../AiKnowledgeDocumentServiceImpl.java | 6 +++ .../ai/service/model/AiChatRoleService.java | 8 ++-- .../service/model/AiChatRoleServiceImpl.java | 37 +++++++++++++++++-- 11 files changed, 100 insertions(+), 10 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index cca856fe17..c52cfe2cf7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -10,8 +11,10 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowl import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -22,6 +25,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - AI 知识库文档") @RestController @@ -91,4 +95,13 @@ public class AiKnowledgeDocumentController { return success(true); } + @GetMapping("/simple-list") + @Operation(summary = "获得知识库文档的精简列表") + public CommonResult> getKnowledgeDocumentSimpleList() { + List list = documentService + .getKnowledgeDocumentListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, document -> new AiKnowledgeDocumentRespVO() + .setId(document.getId()).setName(document.getName()))); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java index 02f698b944..5714c5fedd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java @@ -75,7 +75,7 @@ public class AiChatRoleController { @GetMapping("/category-list") @Operation(summary = "获得聊天角色的分类列表") public CommonResult> getChatRoleCategoryList() { - return success(chatRoleService.getChatRoleCategoryList()); + return success(chatRoleService.getChatRoleCategoryList()); } // ========== 角色管理 ========== diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java index d47c66d2bd..cbbed87c04 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java @@ -8,6 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - AI 聊天角色 Response VO") @Data @@ -20,7 +21,7 @@ public class AiChatRoleRespVO implements VO { private Long userId; @Schema(description = "模型编号", example = "17640") - @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = {"name", "model"}, refs = {"modelName", "model"}) + @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { "name", "model" }, refs = { "modelName", "model" }) private Long modelId; @Schema(description = "模型名字", example = "张三") private String modelName; @@ -45,6 +46,9 @@ public class AiChatRoleRespVO implements VO { @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED) private String systemMessage; + @Schema(description = "知识库文档编号列表", example = "1,2,3") + private List documentIds; + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Boolean publicStatus; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java index 4673901d38..5f802202a8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java @@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotEmpty; import lombok.Data; import org.hibernate.validator.constraints.URL; +import java.util.List; + @Schema(description = "管理后台 - AI 聊天角色新增/修改【我的】 Request VO") @Data public class AiChatRoleSaveMyReqVO { @@ -29,4 +31,7 @@ public class AiChatRoleSaveMyReqVO { @NotEmpty(message = "角色设定不能为空") private String systemMessage; + @Schema(description = "知识库文档编号列表", example = "1,2,3") + private List documentIds; + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java index bdda027ef2..e7ba7fd2d1 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java @@ -7,6 +7,8 @@ import lombok.*; import jakarta.validation.constraints.*; import org.hibernate.validator.constraints.URL; +import java.util.List; + @Schema(description = "管理后台 - AI 聊天角色新增/修改 Request VO") @Data public class AiChatRoleSaveReqVO { @@ -42,6 +44,9 @@ public class AiChatRoleSaveReqVO { @NotEmpty(message = "角色设定不能为空") private String systemMessage; + @Schema(description = "知识库文档编号列表", example = "1,2,3") + private List documentIds; + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "是否公开不能为空") private Boolean publicStatus; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java index fd35b85795..006057e353 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java @@ -2,11 +2,16 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; 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 lombok.*; +import java.util.List; + /** * AI 聊天角色 DO * @@ -62,6 +67,14 @@ public class AiChatRoleDO extends BaseDO { */ private Long modelId; + /** + * 知识库文档编号列表 + * + * 关联 {@link AiKnowledgeDocumentDO#getId()} 字段 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List documentIds; + /** * 是否公开 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java index eeacca6345..11a76cc57b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java @@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; +import java.util.List; /** * AI 知识库文档 Mapper @@ -26,9 +27,13 @@ public interface AiKnowledgeDocumentMapper extends BaseMapperX ids) { - update( new LambdaUpdateWrapper() + update(new LambdaUpdateWrapper() .setSql(" retrieval_count = retrieval_count + 1") .in(AiKnowledgeDocumentDO::getId, ids)); } + default List selectListByStatus(Integer status) { + return selectList(AiKnowledgeDocumentDO::getStatus, status); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index 8ff137b331..e1a45efc1a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -115,4 +115,12 @@ public interface AiKnowledgeDocumentService { return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId); } + /** + * 获取指定状态的文档列表 + * + * @param status 状态 + * @return 文档列表 + */ + List getKnowledgeDocumentListByStatus(Integer status); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 48dd78dbb9..3e47720711 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; @@ -210,4 +211,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return knowledgeDocumentMapper.selectByIds(ids); } + @Override + public List getKnowledgeDocumentListByStatus(Integer status) { + return knowledgeDocumentMapper.selectListByStatus(status); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java index 81c8d259b6..e7fecf6ac7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java @@ -32,7 +32,7 @@ public interface AiChatRoleService { * 创建【我的】聊天角色 * * @param createReqVO 创建信息 - * @param userId 用户编号 + * @param userId 用户编号 * @return 编号 */ Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId); @@ -48,7 +48,7 @@ public interface AiChatRoleService { * 创建【我的】聊天角色 * * @param updateReqVO 更新信息 - * @param userId 用户编号 + * @param userId 用户编号 */ void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId); @@ -62,7 +62,7 @@ public interface AiChatRoleService { /** * 删除【我的】聊天角色 * - * @param id 编号 + * @param id 编号 * @param userId 用户编号 */ void deleteChatRoleMy(Long id, Long userId); @@ -106,7 +106,7 @@ public interface AiChatRoleService { * 获得【我的】聊天角色分页 * * @param pageReqVO 分页查询 - * @param userId 用户编号 + * @param userId 用户编号 * @return 聊天角色分页 */ PageResult getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java index 2cf4d46d1c..ce93d4e1de 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java @@ -5,12 +5,14 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -35,8 +37,15 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { @Resource private AiChatRoleMapper chatRoleMapper; + @Resource + private AiKnowledgeDocumentService knowledgeDocumentService; + @Override public Long createChatRole(AiChatRoleSaveReqVO createReqVO) { + // 校验文档 + validateDocuments(createReqVO.getDocumentIds()); + + // 保存角色 AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class); chatRoleMapper.insert(chatRole); return chatRole.getId(); @@ -44,6 +53,10 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { @Override public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) { + // 校验文档 + validateDocuments(createReqVO.getDocumentIds()); + + // 保存角色 AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId) .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false); chatRoleMapper.insert(chatRole); @@ -54,7 +67,10 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) { // 校验存在 validateChatRoleExists(updateReqVO.getId()); - // 更新 + // 校验文档 + validateDocuments(updateReqVO.getDocumentIds()); + + // 更新角色 AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); chatRoleMapper.updateById(updateObj); } @@ -66,12 +82,27 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) { throw exception(CHAT_ROLE_NOT_EXISTS); } + // 校验文档 + validateDocuments(updateReqVO.getDocumentIds()); // 更新 AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); chatRoleMapper.updateById(updateObj); } + /** + * 校验文档是否存在 + * + * @param documentIds 文档编号列表 + */ + private void validateDocuments(List documentIds) { + if (CollUtil.isEmpty(documentIds)) { + return; + } + // 校验文档是否存在 + documentIds.forEach(knowledgeDocumentService::validateKnowledgeDocumentExists); + } + @Override public void deleteChatRole(Long id) { // 校验存在 @@ -134,7 +165,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { @Override public List getChatRoleCategoryList() { List list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus()); - return convertList(list, AiChatRoleDO::getCategory, role -> role != null && StrUtil.isNotBlank(role.getCategory())); + return convertList(list, AiChatRoleDO::getCategory, + role -> role != null && StrUtil.isNotBlank(role.getCategory())); } @Override @@ -143,4 +175,3 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { } } - From 3bb57edc85c48dcb6475ce7c7b98395a3682054b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 12:00:04 +0800 Subject: [PATCH 253/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E8=81=8A=E5=A4=A9=E8=A7=92?= =?UTF-8?q?=E8=89=B2=EF=BC=8C=E6=96=B0=E5=A2=9E=20knowledge=20=E7=9A=84?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E7=BB=91=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledge/AiKnowledgeController.java | 12 +++++++++ .../AiKnowledgeDocumentController.java | 19 +------------- .../model/vo/chatRole/AiChatRoleRespVO.java | 4 +-- .../vo/chatRole/AiChatRoleSaveMyReqVO.java | 4 +-- .../vo/chatRole/AiChatRoleSaveReqVO.java | 4 +-- .../ai/dal/dataobject/model/AiChatRoleDO.java | 8 +++--- .../mysql/knowledge/AiKnowledgeMapper.java | 6 +++++ .../knowledge/AiKnowledgeDocumentService.java | 8 ------ .../AiKnowledgeDocumentServiceImpl.java | 6 ----- .../service/knowledge/AiKnowledgeService.java | 10 +++++++ .../knowledge/AiKnowledgeServiceImpl.java | 7 +++++ .../service/model/AiChatRoleServiceImpl.java | 26 +++++++++---------- 12 files changed, 59 insertions(+), 55 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index f3dc6f022e..39fb8d9430 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -17,7 +18,10 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.List; + import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; // TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库") @@ -62,4 +66,12 @@ public class AiKnowledgeController { return success(true); } + @GetMapping("/simple-list") + @Operation(summary = "获得知识库的精简列表") + public CommonResult> getKnowledgeSimpleList() { + List list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, knowledge -> new AiKnowledgeRespVO() + .setId(knowledge.getId()).setName(knowledge.getName()))); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java index c52cfe2cf7..68fe49a8a7 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java @@ -1,20 +1,13 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; @@ -25,7 +18,6 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - AI 知识库文档") @RestController @@ -95,13 +87,4 @@ public class AiKnowledgeDocumentController { return success(true); } - @GetMapping("/simple-list") - @Operation(summary = "获得知识库文档的精简列表") - public CommonResult> getKnowledgeDocumentSimpleList() { - List list = documentService - .getKnowledgeDocumentListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, document -> new AiKnowledgeDocumentRespVO() - .setId(document.getId()).setName(document.getName()))); - } - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java index cbbed87c04..908e9dcce4 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java @@ -46,8 +46,8 @@ public class AiChatRoleRespVO implements VO { @Schema(description = "角色设定", requiredMode = Schema.RequiredMode.REQUIRED) private String systemMessage; - @Schema(description = "知识库文档编号列表", example = "1,2,3") - private List documentIds; + @Schema(description = "引用的知识库编号列表", example = "1,2,3") + private List knowledgeIds; @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Boolean publicStatus; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java index 5f802202a8..a104421f5d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java @@ -31,7 +31,7 @@ public class AiChatRoleSaveMyReqVO { @NotEmpty(message = "角色设定不能为空") private String systemMessage; - @Schema(description = "知识库文档编号列表", example = "1,2,3") - private List documentIds; + @Schema(description = "引用的知识库编号列表", example = "1,2,3") + private List knowledgeIds; } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java index e7ba7fd2d1..6851564e06 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java @@ -44,8 +44,8 @@ public class AiChatRoleSaveReqVO { @NotEmpty(message = "角色设定不能为空") private String systemMessage; - @Schema(description = "知识库文档编号列表", example = "1,2,3") - private List documentIds; + @Schema(description = "引用的知识库编号列表", example = "1,2,3") + private List knowledgeIds; @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "是否公开不能为空") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java index 006057e353..81a679a2f3 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.model; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; @@ -68,12 +68,12 @@ public class AiChatRoleDO extends BaseDO { private Long modelId; /** - * 知识库文档编号列表 + * 引用的知识库编号列表 * - * 关联 {@link AiKnowledgeDocumentDO#getId()} 字段 + * 关联 {@link AiKnowledgeDO#getId()} 字段 */ @TableField(typeHandler = LongListTypeHandler.class) - private List documentIds; + private List knowledgeIds; /** * 是否公开 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java index 0d6022ed07..3433c0b973 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * AI 知识库 Mapper * @@ -23,4 +25,8 @@ public interface AiKnowledgeMapper extends BaseMapperX { .orderByDesc(AiKnowledgeDO::getId)); } + default List selectListByStatus(Integer status) { + return selectList(AiKnowledgeDO::getStatus, status); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java index e1a45efc1a..8ff137b331 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java @@ -115,12 +115,4 @@ public interface AiKnowledgeDocumentService { return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId); } - /** - * 获取指定状态的文档列表 - * - * @param status 状态 - * @return 文档列表 - */ - List getKnowledgeDocumentListByStatus(Integer status); - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 3e47720711..48dd78dbb9 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO; @@ -211,9 +210,4 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic return knowledgeDocumentMapper.selectByIds(ids); } - @Override - public List getKnowledgeDocumentListByStatus(Integer status) { - return knowledgeDocumentMapper.selectListByStatus(status); - } - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java index d1a7f7d1c4..5336570d27 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java @@ -5,6 +5,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; +import java.util.List; + /** * AI 知识库-基础信息 Service 接口 * @@ -50,4 +52,12 @@ public interface AiKnowledgeService { */ PageResult getKnowledgePage(AiKnowledgePageReqVO pageReqVO); + /** + * 获得指定状态的知识库列表 + * + * @param status 状态 + * @return 知识库列表 + */ + List getKnowledgeSimpleListByStatus(Integer status); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 0bd4029850..448a0d6ba1 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -12,6 +12,8 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.List; + import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS; @@ -76,4 +78,9 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { return knowledgeMapper.selectPage(pageReqVO); } + @Override + public List getKnowledgeSimpleListByStatus(Integer status) { + return knowledgeMapper.selectListByStatus(status); + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java index ce93d4e1de..f0e07f2856 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java @@ -5,14 +5,13 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper; -import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -23,7 +22,8 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_DISABLE; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS; /** * AI 聊天角色 Service 实现类 @@ -38,12 +38,12 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { private AiChatRoleMapper chatRoleMapper; @Resource - private AiKnowledgeDocumentService knowledgeDocumentService; + private AiKnowledgeService knowledgeService; @Override public Long createChatRole(AiChatRoleSaveReqVO createReqVO) { // 校验文档 - validateDocuments(createReqVO.getDocumentIds()); + validateDocuments(createReqVO.getKnowledgeIds()); // 保存角色 AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class); @@ -54,7 +54,7 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { @Override public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) { // 校验文档 - validateDocuments(createReqVO.getDocumentIds()); + validateDocuments(createReqVO.getKnowledgeIds()); // 保存角色 AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId) @@ -68,7 +68,7 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { // 校验存在 validateChatRoleExists(updateReqVO.getId()); // 校验文档 - validateDocuments(updateReqVO.getDocumentIds()); + validateDocuments(updateReqVO.getKnowledgeIds()); // 更新角色 AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); @@ -83,7 +83,7 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { throw exception(CHAT_ROLE_NOT_EXISTS); } // 校验文档 - validateDocuments(updateReqVO.getDocumentIds()); + validateDocuments(updateReqVO.getKnowledgeIds()); // 更新 AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); @@ -91,16 +91,16 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { } /** - * 校验文档是否存在 + * 校验知识库是否存在 * - * @param documentIds 文档编号列表 + * @param knowledgeIds 知识库编号列表 */ - private void validateDocuments(List documentIds) { - if (CollUtil.isEmpty(documentIds)) { + private void validateDocuments(List knowledgeIds) { + if (CollUtil.isEmpty(knowledgeIds)) { return; } // 校验文档是否存在 - documentIds.forEach(knowledgeDocumentService::validateKnowledgeDocumentExists); + knowledgeIds.forEach(knowledgeService::validateKnowledgeExists); } @Override From ff9267ad759a819ba97cdd19c5264cb47ad23043 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 9 Mar 2025 12:53:54 +0800 Subject: [PATCH 254/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=20CRUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 3 + .../admin/rule/IotDataBridgeController.java | 93 +++++++++++++++++++ .../vo/databridge/IotDataBridgePageReqVO.java | 39 ++++++++ .../vo/databridge/IotDataBridgeRespVO.java | 52 +++++++++++ .../vo/databridge/IotDataBridgeSaveReqVO.java | 37 ++++++++ .../admin/rule/vo/package-info.java | 1 + .../dal/mysql/rule/IotDataBridgeMapper.java | 22 ++++- .../service/rule/IotDataBridgeService.java | 48 ++++++++-- .../rule/IotDataBridgeServiceImpl.java | 67 ++++++++----- .../action/IotRuleSceneDataBridgeAction.java | 2 +- 10 files changed, 334 insertions(+), 30 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 3d12c4b594..7b84c66fcf 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -70,4 +70,7 @@ public interface ErrorCodeConstants { // ========== MQTT 通信相关 1-050-009-000 ========== ErrorCode MQTT_TOPIC_ILLEGAL = new ErrorCode(1_050_009_000, "topic illegal"); + // ========== IoT 数据桥梁 1-050-010-000 ========== + ErrorCode DATA_BRIDGE_NOT_EXISTS = new ErrorCode(1_050_010_000, "IoT 数据桥梁不存在"); + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java new file mode 100644 index 0000000000..2d2f8fb75b --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeRespVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IoT 数据桥梁") +@RestController +@RequestMapping("/iot/data-bridge") +@Validated +public class IotDataBridgeController { + + @Resource + private IotDataBridgeService dataBridgeService; + + @PostMapping("/create") + @Operation(summary = "创建IoT 数据桥梁") + @PreAuthorize("@ss.hasPermission('iot:data-bridge:create')") + public CommonResult createDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO createReqVO) { + return success(dataBridgeService.createDataBridge(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新IoT 数据桥梁") + @PreAuthorize("@ss.hasPermission('iot:data-bridge:update')") + public CommonResult updateDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO updateReqVO) { + dataBridgeService.updateDataBridge(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除IoT 数据桥梁") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:data-bridge:delete')") + public CommonResult deleteDataBridge(@RequestParam("id") Long id) { + dataBridgeService.deleteDataBridge(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得IoT 数据桥梁") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')") + public CommonResult getDataBridge(@RequestParam("id") Long id) { + IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(id); + return success(BeanUtils.toBean(dataBridge, IotDataBridgeRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得IoT 数据桥梁分页") + @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')") + public CommonResult> getDataBridgePage(@Valid IotDataBridgePageReqVO pageReqVO) { + PageResult pageResult = dataBridgeService.getDataBridgePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出IoT 数据桥梁 Excel") + @PreAuthorize("@ss.hasPermission('iot:data-bridge:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportDataBridgeExcel(@Valid IotDataBridgePageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = dataBridgeService.getDataBridgePage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "IoT 数据桥梁.xls", "数据", IotDataBridgeRespVO.class, + BeanUtils.toBean(list, IotDataBridgeRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java new file mode 100644 index 0000000000..7da87be07a --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - IoT 数据桥梁分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class IotDataBridgePageReqVO extends PageParam { + + @Schema(description = "桥梁名称", example = "赵六") + private String name; + + @Schema(description = "桥梁描述", example = "随便") + private String description; + + @Schema(description = "桥梁状态", example = "2") + private Integer status; + + @Schema(description = "桥梁方向") + private Integer direction; + + @Schema(description = "桥梁类型", example = "1") + private Integer type; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java new file mode 100644 index 0000000000..85247358c4 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - IoT 数据桥梁 Response VO") +@Data +@ExcelIgnoreUnannotated +public class IotDataBridgeRespVO { + + @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") + @ExcelProperty("桥梁编号") + private Long id; + + @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @ExcelProperty("桥梁名称") + private String name; + + @Schema(description = "桥梁描述", example = "随便") + @ExcelProperty("桥梁描述") + private String description; + + @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty(value = "桥梁状态", converter = DictConvert.class) + @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer status; + + @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty(value = "桥梁方向", converter = DictConvert.class) + @DictFormat("iot_data_bridg_direction_enum") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer direction; + + @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "桥梁类型", converter = DictConvert.class) + @DictFormat("iot_data_bridg_type_enum") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer type; + + @Schema(description = "桥梁配置") + @ExcelProperty("桥梁配置") + private String config; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java new file mode 100644 index 0000000000..2fd8b36093 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 数据桥梁新增/修改 Request VO") +@Data +public class IotDataBridgeSaveReqVO { + + @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") + private Long id; + + @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotEmpty(message = "桥梁名称不能为空") + private String name; + + @Schema(description = "桥梁描述", example = "随便") + private String description; + + @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "桥梁状态不能为空") + private Integer status; + + @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "桥梁方向不能为空") + private Integer direction; + + @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "桥梁类型不能为空") + private Integer type; + + @Schema(description = "桥梁配置") + private String config; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java new file mode 100644 index 0000000000..a977d86e93 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java index 076b341f00..1609aec34a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java @@ -1,9 +1,29 @@ package cn.iocoder.yudao.module.iot.dal.mysql.rule; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import org.apache.ibatis.annotations.Mapper; +/** + * IoT 数据桥梁 Mapper + * + * @author HUIHUI + */ @Mapper public interface IotDataBridgeMapper extends BaseMapperX { -} + + default PageResult selectPage(IotDataBridgePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(IotDataBridgeDO::getName, reqVO.getName()) + .likeIfPresent(IotDataBridgeDO::getDescription, reqVO.getDescription()) + .eqIfPresent(IotDataBridgeDO::getStatus, reqVO.getStatus()) + .eqIfPresent(IotDataBridgeDO::getDirection, reqVO.getDirection()) + .eqIfPresent(IotDataBridgeDO::getType, reqVO.getType()) + .betweenIfPresent(IotDataBridgeDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(IotDataBridgeDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java index cbf0b36c23..f720a1d15d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java @@ -1,20 +1,54 @@ package cn.iocoder.yudao.module.iot.service.rule; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import jakarta.validation.Valid; /** - * IoT 数据桥梁的 Service 接口 + * IoT 数据桥梁 Service 接口 * - * @author 芋道源码 + * @author HUIHUI */ public interface IotDataBridgeService { /** - * 获得指定数据桥梁 + * 创建IoT 数据桥梁 * - * @param id 数据桥梁编号 - * @return 数据桥梁 + * @param createReqVO 创建信息 + * @return 编号 */ - IotDataBridgeDO getIotDataBridge(Long id); + Long createDataBridge(@Valid IotDataBridgeSaveReqVO createReqVO); -} + /** + * 更新IoT 数据桥梁 + * + * @param updateReqVO 更新信息 + */ + void updateDataBridge(@Valid IotDataBridgeSaveReqVO updateReqVO); + + /** + * 删除IoT 数据桥梁 + * + * @param id 编号 + */ + void deleteDataBridge(Long id); + + /** + * 获得IoT 数据桥梁 + * + * @param id 编号 + * @return IoT 数据桥梁 + */ + IotDataBridgeDO getDataBridge(Long id); + + /** + * 获得IoT 数据桥梁分页 + * + * @param pageReqVO 分页查询 + * @return IoT 数据桥梁分页 + */ + PageResult getDataBridgePage(IotDataBridgePageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java index 7814fd9bbc..9e439fc996 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeServiceImpl.java @@ -1,45 +1,70 @@ package cn.iocoder.yudao.module.iot.service.rule; -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotDataBridgeMapper; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; -import java.util.Objects; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DATA_BRIDGE_NOT_EXISTS; /** - * IoT 数据桥梁的 Service 实现类 + * IoT 数据桥梁 Service 实现类 * - * @author 芋道源码 + * @author HUIHUI */ @Service @Validated -@Slf4j public class IotDataBridgeServiceImpl implements IotDataBridgeService { @Resource private IotDataBridgeMapper dataBridgeMapper; - // TODO @芋艿:临时测试 @Override - public IotDataBridgeDO getIotDataBridge(Long id) { - if (Objects.equals(id, 1L)) { - IotDataBridgeDO.HttpConfig config = new IotDataBridgeDO.HttpConfig() - .setUrl("http://127.0.0.1:48080/test") -// .setMethod("POST") - .setMethod("GET") - .setQuery(MapUtil.of("aaa", "bbb")) - .setHeaders(MapUtil.of("ccc", "ddd")) - .setBody(JsonUtils.toJsonString(MapUtil.of("eee", "fff"))); - return IotDataBridgeDO.builder().id(1L).name("芋道").description("芋道源码").status(0).direction(1) - .type(IotDataBridgTypeEnum.HTTP.getType()).config(config).build(); + public Long createDataBridge(IotDataBridgeSaveReqVO createReqVO) { + // 插入 + IotDataBridgeDO dataBridge = BeanUtils.toBean(createReqVO, IotDataBridgeDO.class); + dataBridgeMapper.insert(dataBridge); + // 返回 + return dataBridge.getId(); + } + + @Override + public void updateDataBridge(IotDataBridgeSaveReqVO updateReqVO) { + // 校验存在 + validateDataBridgeExists(updateReqVO.getId()); + // 更新 + IotDataBridgeDO updateObj = BeanUtils.toBean(updateReqVO, IotDataBridgeDO.class); + dataBridgeMapper.updateById(updateObj); + } + + @Override + public void deleteDataBridge(Long id) { + // 校验存在 + validateDataBridgeExists(id); + // 删除 + dataBridgeMapper.deleteById(id); + } + + private void validateDataBridgeExists(Long id) { + if (dataBridgeMapper.selectById(id) == null) { + throw exception(DATA_BRIDGE_NOT_EXISTS); } + } + + @Override + public IotDataBridgeDO getDataBridge(Long id) { return dataBridgeMapper.selectById(id); } -} + @Override + public PageResult getDataBridgePage(IotDataBridgePageReqVO pageReqVO) { + return dataBridgeMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java index d94922f5d0..b38e181f93 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneDataBridgeAction.java @@ -36,7 +36,7 @@ public class IotRuleSceneDataBridgeAction implements IotRuleSceneAction { } // 1.2 获得数据桥梁 Assert.notNull(config.getDataBridgeId(), "数据桥梁编号不能为空"); - IotDataBridgeDO dataBridge = dataBridgeService.getIotDataBridge(config.getDataBridgeId()); + IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(config.getDataBridgeId()); if (dataBridge == null || dataBridge.getConfig() == null) { log.error("[execute][message({}) config({}) 对应的数据桥梁不存在]", message, config); return; From b1d3b73b6d73d99808a24797831a5cab58213844 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 9 Mar 2025 13:29:12 +0800 Subject: [PATCH 255/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=20config=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/DictTypeConstants.java | 3 + ...m.java => IotDataBridgeDirectionEnum.java} | 4 +- ...peEnum.java => IotDataBridgeTypeEnum.java} | 25 +- .../vo/databridge/IotDataBridgeRespVO.java | 13 +- .../vo/databridge/IotDataBridgeSaveReqVO.java | 4 +- .../config/IotDataBridgeConfig.java | 32 +++ .../config/IotDataBridgeHttpConfig.java | 36 +++ .../config/IotDataBridgeKafkaMQConfig.java | 35 +++ .../config/IotDataBridgeMqttConfig.java | 34 +++ .../config/IotDataBridgeRabbitMQConfig.java | 46 ++++ .../IotDataBridgeRedisStreamMQConfig.java | 34 +++ .../config/IotDataBridgeRocketMQConfig.java | 39 ++++ .../dal/dataobject/rule/IotDataBridgeDO.java | 217 +----------------- .../databridge/IotHttpDataBridgeExecute.java | 10 +- .../IotKafkaMQDataBridgeExecute.java | 13 +- .../IotRabbitMQDataBridgeExecute.java | 13 +- .../IotRedisStreamMQDataBridgeExecute.java | 13 +- .../IotRocketMQDataBridgeExecute.java | 13 +- 18 files changed, 325 insertions(+), 259 deletions(-) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/{IotDataBridgDirectionEnum.java => IotDataBridgeDirectionEnum.java} (78%) rename yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/{IotDataBridgTypeEnum.java => IotDataBridgeTypeEnum.java} (53%) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java index 04df143bed..fc442d2b35 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -15,5 +15,8 @@ public class DictTypeConstants { public static final String VALIDATE_TYPE = "iot_validate_type"; public static final String DEVICE_STATE = "iot_device_state"; + public static final String IOT_DATA_BRIDGE_DIRECTION_ENUM = "iot_data_bridge_direction_enum"; + public static final String IOT_DATA_BRIDGE_TYPE_ENUM = "iot_data_bridge_type_enum"; + } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java similarity index 78% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java index ff4993d0ea..eb4b999163 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgDirectionEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java @@ -13,14 +13,14 @@ import java.util.Arrays; */ @RequiredArgsConstructor @Getter -public enum IotDataBridgDirectionEnum implements ArrayValuable { +public enum IotDataBridgeDirectionEnum implements ArrayValuable { INPUT(1), // 输入 OUTPUT(2); // 输出 private final Integer type; - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgDirectionEnum::getType).toArray(Integer[]::new); + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeDirectionEnum::getType).toArray(Integer[]::new); @Override public Integer[] array() { diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java similarity index 53% rename from yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java rename to yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java index 295f35cffd..25c7e8c1fe 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java @@ -13,25 +13,26 @@ import java.util.Arrays; */ @RequiredArgsConstructor @Getter -public enum IotDataBridgTypeEnum implements ArrayValuable { +public enum IotDataBridgeTypeEnum implements ArrayValuable { - HTTP(1), - TCP(2), - WEBSOCKET(3), + HTTP(1, "HTTP"), + TCP(2, "TCP"), + WEBSOCKET(3, "WEBSOCKET"), - MQTT(10), + MQTT(10, "MQTT"), - DATABASE(20), - REDIS_STREAM(21), + DATABASE(20, "DATABASE"), + REDIS_STREAM(21, "REDIS_STREAM"), - ROCKETMQ(30), - RABBITMQ(31), - KAFKA(32) - ; + ROCKETMQ(30, "ROCKETMQ"), + RABBITMQ(31, "RABBITMQ"), + KAFKA(32, "KAFKA"); private final Integer type; - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgTypeEnum::getType).toArray(Integer[]::new); + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotDataBridgeTypeEnum::getType).toArray(Integer[]::new); @Override public Integer[] array() { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java index 85247358c4..3e50dc4d5b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,6 +10,10 @@ import lombok.Data; import java.time.LocalDateTime; +import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.IOT_DATA_BRIDGE_DIRECTION_ENUM; +import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.IOT_DATA_BRIDGE_TYPE_ENUM; +import static cn.iocoder.yudao.module.system.enums.DictTypeConstants.COMMON_STATUS; + @Schema(description = "管理后台 - IoT 数据桥梁 Response VO") @Data @ExcelIgnoreUnannotated @@ -28,22 +33,22 @@ public class IotDataBridgeRespVO { @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @ExcelProperty(value = "桥梁状态", converter = DictConvert.class) - @DictFormat("common_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + @DictFormat(COMMON_STATUS) private Integer status; @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty(value = "桥梁方向", converter = DictConvert.class) - @DictFormat("iot_data_bridg_direction_enum") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + @DictFormat(IOT_DATA_BRIDGE_DIRECTION_ENUM) private Integer direction; @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @ExcelProperty(value = "桥梁类型", converter = DictConvert.class) - @DictFormat("iot_data_bridg_type_enum") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + @DictFormat(IOT_DATA_BRIDGE_TYPE_ENUM) private Integer type; @Schema(description = "桥梁配置") @ExcelProperty("桥梁配置") - private String config; + private IotDataBridgeConfig config; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java index 2fd8b36093..96620a1ca4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -32,6 +33,7 @@ public class IotDataBridgeSaveReqVO { private Integer type; @Schema(description = "桥梁配置") - private String config; + @NotNull(message = "桥梁配置不能为空") + private IotDataBridgeConfig config; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java new file mode 100644 index 0000000000..f37a51edb3 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; + +/** + * 抽象类 IotDataBridgeConfig + * + * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类。 + * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 + * + * @author HUIHUI + */ +@Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "HTTP"), + @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "KAFKA"), + @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "MQTT"), + @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "RABBITMQ"), + @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "REDIS_STREAM"), + @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "ROCKETMQ"), +}) +public abstract class IotDataBridgeConfig { + + /** + * 配置类型 + */ + private String type; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java new file mode 100644 index 0000000000..69cdc71f7f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import lombok.Data; + +import java.util.Map; + +/** + * HTTP 配置 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeHttpConfig extends IotDataBridgeConfig { + + /** + * 请求 URL + */ + private String url; + /** + * 请求方法 + */ + private String method; + /** + * 请求头 + */ + private Map headers; + /** + * 请求参数 + */ + private Map query; + /** + * 请求体 + */ + private String body; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java new file mode 100644 index 0000000000..3acd646f33 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import lombok.Data; + +/** + * Kafka 配置 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeKafkaMQConfig extends IotDataBridgeConfig { + + /** + * Kafka 服务器地址 + */ + private String bootstrapServers; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 是否启用 SSL + */ + private Boolean ssl; + + /** + * 主题 + */ + private String topic; + +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java new file mode 100644 index 0000000000..0bf7067bc1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import lombok.Data; + +/** + * MQTT 配置 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeMqttConfig extends IotDataBridgeConfig { + + /** + * MQTT 服务器地址 + */ + private String url; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 客户端编号 + */ + private String clientId; + /** + * 主题 + */ + private String topic; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java new file mode 100644 index 0000000000..29bf328979 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import lombok.Data; + +/** + * RabbitMQ 配置 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeRabbitMQConfig extends IotDataBridgeConfig { + + /** + * RabbitMQ 服务器地址 + */ + private String host; + /** + * 端口 + */ + private Integer port; + /** + * 虚拟主机 + */ + private String virtualHost; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + + /** + * 交换机名称 + */ + private String exchange; + /** + * 路由键 + */ + private String routingKey; + /** + * 队列名称 + */ + private String queue; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java new file mode 100644 index 0000000000..db7b2b2bcb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import lombok.Data; + +/** + * Redis Stream MQ 配置 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeRedisStreamMQConfig extends IotDataBridgeConfig { + + /** + * Redis 服务器地址 + */ + private String host; + /** + * 端口 + */ + private Integer port; + /** + * 密码 + */ + private String password; + /** + * 数据库索引 + */ + private Integer database; + + /** + * 主题 + */ + private String topic; +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java new file mode 100644 index 0000000000..e911461e4d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; + +import lombok.Data; + +/** + * RocketMQ 配置 + * + * @author HUIHUI + */ +@Data +public class IotDataBridgeRocketMQConfig extends IotDataBridgeConfig { + + /** + * RocketMQ 名称服务器地址 + */ + private String nameServer; + /** + * 访问密钥 + */ + private String accessKey; + /** + * 秘密钥匙 + */ + private String secretKey; + + /** + * 生产者组 + */ + private String group; + /** + * 主题 + */ + private String topic; + /** + * 标签 + */ + private String tags; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index bb0623a1ff..ef0a60ecab 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -1,16 +1,16 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; 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.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.*; -import java.util.Map; - /** * IoT 数据桥梁 DO * @@ -48,14 +48,14 @@ public class IotDataBridgeDO extends BaseDO { /** * 桥梁方向 * - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgDirectionEnum} + * 枚举 {@link IotDataBridgeDirectionEnum} */ private Integer direction; /** * 桥梁类型 * - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum} + * 枚举 {@link IotDataBridgeTypeEnum} */ private Integer type; @@ -63,211 +63,6 @@ public class IotDataBridgeDO extends BaseDO { * 桥梁配置 */ @TableField(typeHandler = JacksonTypeHandler.class) - private Config config; - - /** - * 文件客户端的配置 - * 不同实现的客户端,需要不同的配置,通过子类来定义 - * - * @author 芋道源码 - */ - @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) - // @JsonTypeInfo 注解的作用,Jackson 多态 - // 1. 序列化到时数据库时,增加 @class 属性。 - // 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 - public interface Config { - } - - /** - * HTTP 配置 - */ - @Data - public static class HttpConfig implements Config { - - /** - * 请求 URL - */ - private String url; - /** - * 请求方法 - */ - private String method; - /** - * 请求头 - */ - private Map headers; - /** - * 请求参数 - */ - private Map query; - /** - * 请求体 - */ - private String body; - - } - - /** - * MQTT 配置 - */ - @Data - public static class MqttConfig implements Config { - - /** - * MQTT 服务器地址 - */ - private String url; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 客户端编号 - */ - private String clientId; - /** - * 主题 - */ - private String topic; - - } - - /** - * RocketMQ 配置 - */ - @Data - public static class RocketMQConfig implements Config { - - /** - * RocketMQ 名称服务器地址 - */ - private String nameServer; - /** - * 访问密钥 - */ - private String accessKey; - /** - * 秘密钥匙 - */ - private String secretKey; - - /** - * 生产者组 - */ - private String group; - /** - * 主题 - */ - private String topic; - /** - * 标签 - */ - private String tags; - - } - - /** - * Kafka 配置 - */ - @Data - public static class KafkaMQConfig implements Config { - - /** - * Kafka 服务器地址 - */ - private String bootstrapServers; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * 是否启用 SSL - */ - private Boolean ssl; - - /** - * 主题 - */ - private String topic; - - } - - /** - * RabbitMQ 配置 - */ - @Data - public static class RabbitMQConfig implements Config { - - /** - * RabbitMQ 服务器地址 - */ - private String host; - /** - * 端口 - */ - private Integer port; - /** - * 虚拟主机 - */ - private String virtualHost; - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - - /** - * 交换机名称 - */ - private String exchange; - /** - * 路由键 - */ - private String routingKey; - /** - * 队列名称 - */ - private String queue; - } - - /** - * Redis Stream MQ 配置 - */ - @Data - public static class RedisStreamMQConfig implements Config { - - /** - * Redis 服务器地址 - */ - private String host; - /** - * 端口 - */ - private Integer port; - /** - * 密码 - */ - private String password; - /** - * 数据库索引 - */ - private Integer database; - - /** - * 主题 - */ - private String topic; - } + private IotDataBridgeConfig config; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java index ffe2c5b803..22b72e055e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotHttpDataBridgeExecute.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.util.http.HttpUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeHttpConfig; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -25,19 +25,19 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ */ @Component @Slf4j -public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { +public class IotHttpDataBridgeExecute implements IotDataBridgeExecute { @Resource private RestTemplate restTemplate; @Override public Integer getType() { - return IotDataBridgTypeEnum.HTTP.getType(); + return IotDataBridgeTypeEnum.HTTP.getType(); } @Override @SuppressWarnings({"unchecked", "deprecation"}) - public void execute0(IotDeviceMessage message, IotDataBridgeDO.HttpConfig config) { + public void execute0(IotDeviceMessage message, IotDataBridgeHttpConfig config) { String url = null; HttpMethod method = HttpMethod.valueOf(config.getMethod().toUpperCase()); HttpEntity requestEntity = null; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 6e3ef67a25..3b7f99bf42 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -1,7 +1,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeKafkaMQConfig; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.producer.ProducerConfig; @@ -26,17 +27,17 @@ import java.util.concurrent.TimeUnit; @Component @Slf4j public class IotKafkaMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute> { + AbstractCacheableDataBridgeExecute> { private static final Duration SEND_TIMEOUT = Duration.ofMillis(10000); // 10 秒超时时间 @Override public Integer getType() { - return IotDataBridgTypeEnum.KAFKA.getType(); + return IotDataBridgeTypeEnum.KAFKA.getType(); } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.KafkaMQConfig config) throws Exception { + public void execute0(IotDeviceMessage message, IotDataBridgeKafkaMQConfig config) throws Exception { // 1. 获取或创建 KafkaTemplate KafkaTemplate kafkaTemplate = getProducer(config); @@ -47,7 +48,7 @@ public class IotKafkaMQDataBridgeExecute extends } @Override - protected KafkaTemplate initProducer(IotDataBridgeDO.KafkaMQConfig config) { + protected KafkaTemplate initProducer(IotDataBridgeKafkaMQConfig config) { // 1.1 构建生产者配置 Map props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, config.getBootstrapServers()); @@ -82,7 +83,7 @@ public class IotKafkaMQDataBridgeExecute extends IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute(); // 2. 创建共享的配置 - IotDataBridgeDO.KafkaMQConfig config = new IotDataBridgeDO.KafkaMQConfig(); + IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig(); config.setBootstrapServers("127.0.0.1:9092"); config.setTopic("test-topic"); config.setSsl(false); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 5599144dad..54485c091d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -1,7 +1,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRabbitMQConfig; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; @@ -22,16 +23,16 @@ import java.time.LocalDateTime; @Component @Slf4j public class IotRabbitMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute { + AbstractCacheableDataBridgeExecute { @Override public Integer getType() { - return IotDataBridgTypeEnum.RABBITMQ.getType(); + return IotDataBridgeTypeEnum.RABBITMQ.getType(); } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.RabbitMQConfig config) throws Exception { + public void execute0(IotDeviceMessage message, IotDataBridgeRabbitMQConfig config) throws Exception { // 1. 获取或创建 Channel Channel channel = getProducer(config); @@ -48,7 +49,7 @@ public class IotRabbitMQDataBridgeExecute extends @Override @SuppressWarnings("resource") - protected Channel initProducer(IotDataBridgeDO.RabbitMQConfig config) throws Exception { + protected Channel initProducer(IotDataBridgeRabbitMQConfig config) throws Exception { // 1. 创建连接工厂 ConnectionFactory factory = new ConnectionFactory(); factory.setHost(config.getHost()); @@ -81,7 +82,7 @@ public class IotRabbitMQDataBridgeExecute extends IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute(); // 2. 创建共享的配置 - IotDataBridgeDO.RabbitMQConfig config = new IotDataBridgeDO.RabbitMQConfig(); + IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig(); config.setHost("localhost"); config.setPort(5672); config.setVirtualHost("/"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java index a20334f4fd..5616c7e648 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamMQConfig; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; @@ -30,15 +31,15 @@ import java.time.LocalDateTime; @Component @Slf4j public class IotRedisStreamMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute> { + AbstractCacheableDataBridgeExecute> { @Override public Integer getType() { - return IotDataBridgTypeEnum.REDIS_STREAM.getType(); + return IotDataBridgeTypeEnum.REDIS_STREAM.getType(); } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.RedisStreamMQConfig config) throws Exception { + public void execute0(IotDeviceMessage message, IotDataBridgeRedisStreamMQConfig config) throws Exception { // 1. 获取 RedisTemplate RedisTemplate redisTemplate = getProducer(config); @@ -50,7 +51,7 @@ public class IotRedisStreamMQDataBridgeExecute extends } @Override - protected RedisTemplate initProducer(IotDataBridgeDO.RedisStreamMQConfig config) { + protected RedisTemplate initProducer(IotDataBridgeRedisStreamMQConfig config) { // 1.1 创建 Redisson 配置 Config redissonConfig = new Config(); SingleServerConfig serverConfig = redissonConfig.useSingleServer() @@ -101,7 +102,7 @@ public class IotRedisStreamMQDataBridgeExecute extends IotRedisStreamMQDataBridgeExecute action = new IotRedisStreamMQDataBridgeExecute(); // 2. 创建共享的配置 - IotDataBridgeDO.RedisStreamMQConfig config = new IotDataBridgeDO.RedisStreamMQConfig(); + IotDataBridgeRedisStreamMQConfig config = new IotDataBridgeRedisStreamMQConfig(); config.setHost("127.0.0.1"); config.setPort(6379); config.setDatabase(0); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index 542d190ed5..541bd181e0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -1,7 +1,8 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRocketMQConfig; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; -import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgTypeEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.producer.DefaultMQProducer; @@ -23,15 +24,15 @@ import java.time.LocalDateTime; @Component @Slf4j public class IotRocketMQDataBridgeExecute extends - AbstractCacheableDataBridgeExecute { + AbstractCacheableDataBridgeExecute { @Override public Integer getType() { - return IotDataBridgTypeEnum.ROCKETMQ.getType(); + return IotDataBridgeTypeEnum.ROCKETMQ.getType(); } @Override - public void execute0(IotDeviceMessage message, IotDataBridgeDO.RocketMQConfig config) throws Exception { + public void execute0(IotDeviceMessage message, IotDataBridgeRocketMQConfig config) throws Exception { // 1. 获取或创建 Producer DefaultMQProducer producer = getProducer(config); @@ -52,7 +53,7 @@ public class IotRocketMQDataBridgeExecute extends } @Override - protected DefaultMQProducer initProducer(IotDataBridgeDO.RocketMQConfig config) throws Exception { + protected DefaultMQProducer initProducer(IotDataBridgeRocketMQConfig config) throws Exception { DefaultMQProducer producer = new DefaultMQProducer(config.getGroup()); producer.setNamesrvAddr(config.getNameServer()); producer.start(); @@ -70,7 +71,7 @@ public class IotRocketMQDataBridgeExecute extends IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); // 2. 创建共享的配置 - IotDataBridgeDO.RocketMQConfig config = new IotDataBridgeDO.RocketMQConfig(); + IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig(); config.setNameServer("127.0.0.1:9876"); config.setGroup("test-group"); config.setTopic("test-topic"); From cdf316e7788a232d6d37db2b43aa7d87d360b5bf Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 9 Mar 2025 18:04:46 +0800 Subject: [PATCH 256/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=20config=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java | 1 - .../yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java index fc442d2b35..1fc47c0c00 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -18,5 +18,4 @@ public class DictTypeConstants { public static final String IOT_DATA_BRIDGE_DIRECTION_ENUM = "iot_data_bridge_direction_enum"; public static final String IOT_DATA_BRIDGE_TYPE_ENUM = "iot_data_bridge_type_enum"; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index ef0a60ecab..05493b916f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -16,7 +16,7 @@ import lombok.*; * * @author 芋道源码 */ -@TableName("iot_data_bridge") +@TableName(value = "iot_data_bridge", autoResultMap = true) @KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) From cddaca586360a3cb924190c9ade7c5a55a1a420c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 19:02:17 +0800 Subject: [PATCH 257/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E8=81=8A=E5=A4=A9=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E7=9F=A5=E8=AF=86=E5=BA=93=E7=9A=84?= =?UTF-8?q?=E6=8B=BC=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/enums/AiChatRoleEnum.java | 6 +- .../dataobject/chat/AiChatConversationDO.java | 11 +- .../dal/dataobject/chat/AiChatMessageDO.java | 9 +- .../chat/AiChatConversationServiceImpl.java | 2 +- .../chat/AiChatMessageServiceImpl.java | 169 +++++++++++------- 5 files changed, 111 insertions(+), 86 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java index 6cb98c5629..1479274959 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java @@ -35,11 +35,7 @@ public enum AiChatRoleEnum { ### 微信 除此之外不要任何解释性语句。 """), - - AI_KNOWLEDGE_ROLE("知识库助手", """ - 给你提供一些数据参考:{info},请回答我的问题。 - 请你跟进数据参考与工具返回结果回复用户的请求。 - """); + ; /** * 角色名 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java index a9c956deae..358e994104 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java @@ -1,9 +1,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; @@ -65,14 +64,6 @@ public class AiChatConversationDO extends BaseDO { */ private Long roleId; - // TODO @芋艿:可优化,绑定多个知识库。前提,spring ai 支持 RerankModel 的封装 - /** - * 知识库编号 - *

- * 关联 {@link AiKnowledgeDO#getId()} - */ - private Long knowledgeId; - /** * 模型编号 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java index a026ec8193..d121d85785 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java @@ -1,14 +1,14 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.chat; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; 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 lombok.*; import org.springframework.ai.chat.messages.MessageType; @@ -71,13 +71,12 @@ public class AiChatMessageDO extends BaseDO { */ private Long roleId; - /** - * 段落编号数组 + * 知识库段落编号数组 * * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = LongListTypeHandler.class) private List segmentIds; /** diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java index 6483166a7f..6c35571c8f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java @@ -68,7 +68,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService // 2. 创建 AiChatConversationDO 聊天对话 AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false) - .setModelId(model.getId()).setModel(model.getModel()).setKnowledgeId(createReqVO.getKnowledgeId()) + .setModelId(model.getId()).setModel(model.getModel()) .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts()); if (role != null) { conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage()); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 51bcc45d44..b95f7cd214 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -14,12 +14,14 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; -import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; +import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; +import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; import cn.iocoder.yudao.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -32,13 +34,13 @@ import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.model.StreamingChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; -import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import java.time.LocalDateTime; import java.util.*; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; @@ -56,12 +58,21 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_N @Slf4j public class AiChatMessageServiceImpl implements AiChatMessageService { + /** + * 知识库转 {@link UserMessage} 的内容模版 + */ + private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "使用 标记中的内容作为本次对话的参考:\n\n" + + "%s\n\n" + // 多个 的拼接 + "回答要求:\n- 避免提及你是从 获取的知识。"; + @Resource private AiChatMessageMapper chatMessageMapper; @Resource private AiChatConversationService chatConversationService; @Resource + private AiChatRoleService chatRoleService; + @Resource private AiModelService modalService; @Resource private AiKnowledgeSegmentService knowledgeSegmentService; @@ -69,118 +80,143 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @Transactional(rollbackFor = Exception.class) public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { // 1.1 校验对话存在 - AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId()); + AiChatConversationDO conversation = chatConversationService + .validateChatConversationExists(sendReqVO.getConversationId()); if (ObjUtil.notEqual(conversation.getUserId(), userId)) { throw exception(CHAT_CONVERSATION_NOT_EXISTS); } List historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); // 1.2 校验模型 AiModelDO model = modalService.validateModel(conversation.getModelId()); - ChatModel chatModel = modalService.getChatModel(model.getKeyId()); + ChatModel chatModel = modalService.getChatModel(model.getId()); - // 2. 插入 user 发送消息 + // 2. 知识库找回 + List knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), + conversation); + + // 3. 插入 user 发送消息 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, - userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext()); + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), + null); // 3.1 插入 assistant 接收消息 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, - userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext()); + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), + knowledgeSegments); - // 3.2 召回段落 - List segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId()); - - // 3.3 创建 chat 需要的 Prompt - Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO); + // 3.2 创建 chat 需要的 Prompt + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); ChatResponse chatResponse = chatModel.call(prompt); - // 3.4 段式返回 + // 3.3 段式返回 String newContent = chatResponse.getResult().getOutput().getText(); - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent)); - return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) - .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent)); + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent)); + return new AiChatMessageSendRespVO() + .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) + .setContent(newContent)); } @Override - public Flux> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { + public Flux> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, + Long userId) { // 1.1 校验对话存在 - AiChatConversationDO conversation = chatConversationService.validateChatConversationExists(sendReqVO.getConversationId()); + AiChatConversationDO conversation = chatConversationService + .validateChatConversationExists(sendReqVO.getConversationId()); if (ObjUtil.notEqual(conversation.getUserId(), userId)) { throw exception(CHAT_CONVERSATION_NOT_EXISTS); } List historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId()); // 1.2 校验模型 AiModelDO model = modalService.validateModel(conversation.getModelId()); - StreamingChatModel chatModel = modalService.getChatModel(model.getKeyId()); + StreamingChatModel chatModel = modalService.getChatModel(model.getId()); - // 2. 插入 user 发送消息 + // 2. 知识库找回 + List knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), + conversation); + + // 3. 插入 user 发送消息 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, - userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext()); + userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(), + null); - // 3.1 插入 assistant 接收消息 + // 4.1 插入 assistant 接收消息 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model, - userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext()); + userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(), + knowledgeSegments); - // 3.2 召回段落 - List segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId()); - - // 3.3 构建 Prompt,并进行调用 - Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO); + // 4.2 构建 Prompt,并进行调用 + Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); Flux streamResponse = chatModel.stream(prompt); - // 3.4 流式返回 + // 4.3 流式返回 StringBuffer contentBuffer = new StringBuffer(); return streamResponse.map(chunk -> { String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 contentBuffer.append(newContent); // 响应结果 - return success(new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) - .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent))); + return success(new AiChatMessageSendRespVO() + .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) + .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) + .setContent(newContent))); }).doOnComplete(() -> { // 忽略租户,因为 Flux 异步无法透传租户 - TenantUtils.executeIgnore(() -> - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)) - .setContent(contentBuffer.toString()))); + TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString()))); }).doOnError(throwable -> { log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); // 忽略租户,因为 Flux 异步无法透传租户 - TenantUtils.executeIgnore(() -> - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()))); + TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( + new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()))); }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR))); } - private List recallSegment(String content, Long knowledgeId) { - if (Objects.isNull(knowledgeId)) { + private List recallKnowledgeSegment(String content, + AiChatConversationDO conversation) { + // 1. 查询聊天角色 + if (conversation == null || conversation.getRoleId() == null) { return Collections.emptyList(); } -// return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content)); - return null; - } - - private Prompt buildPrompt(AiChatConversationDO conversation, List messages, List segmentList, - AiModelDO model, AiChatMessageSendReqVO sendReqVO) { - // 1. 构建 Prompt Message 列表 - List chatMessages = new ArrayList<>(); - - // 1.1 召回内容消息构建 - if (CollUtil.isNotEmpty(segmentList)) { - PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage()); - StringBuilder infoBuilder = StrUtil.builder(); - segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent())); - Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString())); - chatMessages.add(message); + AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId()); + if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) { + return Collections.emptyList(); } - // 1.2 system context 角色设定 + // 2. 遍历找回 + List knowledgeSegments = new ArrayList<>(); + for (Long knowledgeId : role.getKnowledgeIds()) { + knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO() + .setKnowledgeId(knowledgeId).setContent(content))); + } + return knowledgeSegments; + } + + private Prompt buildPrompt(AiChatConversationDO conversation, List messages, + List knowledgeSegments, + AiModelDO model, AiChatMessageSendReqVO sendReqVO) { + List chatMessages = new ArrayList<>(); + // 1.1 System Context 角色设定 if (StrUtil.isNotBlank(conversation.getSystemMessage())) { chatMessages.add(new SystemMessage(conversation.getSystemMessage())); } - // 1.3 history message 历史消息 + + // 1.2 历史 history message 历史消息 List contextMessages = filterContextMessages(messages, conversation, sendReqVO); - contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()))); - // 1.4 user message 新发送消息 + contextMessages + .forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()))); + + // 1.3 当前 user message 新发送消息 chatMessages.add(new UserMessage(sendReqVO.getContent())); + // 1.4 知识库,通过 UserMessage 实现 + if (CollUtil.isNotEmpty(knowledgeSegments)) { + String reference = knowledgeSegments.stream() + .map(segment -> "\n" + segment.getContent() + "") + .collect(Collectors.joining("\n\n")); + chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference))); + } + // 2. 构建 ChatOptions 对象 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), @@ -199,8 +235,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { * @return 消息上下文 */ private List filterContextMessages(List messages, - AiChatConversationDO conversation, - AiChatMessageSendReqVO sendReqVO) { + AiChatConversationDO conversation, + AiChatMessageSendReqVO sendReqVO) { if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) { return Collections.emptyList(); } @@ -211,7 +247,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { continue; } AiChatMessageDO userMessage = CollUtil.get(messages, i - 1); - if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId()) + if (userMessage == null + || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId()) || StrUtil.isEmpty(assistantMessage.getContent())) { continue; } @@ -228,11 +265,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, - AiModelDO model, Long userId, Long roleId, - MessageType messageType, String content, Boolean useContext) { + AiModelDO model, Long userId, Long roleId, + MessageType messageType, String content, Boolean useContext, + List knowledgeSegments) { AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) - .setType(messageType.getValue()).setContent(content).setUseContext(useContext); + .setType(messageType.getValue()).setContent(content).setUseContext(useContext) + .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId)); message.setCreateTime(LocalDateTime.now()); chatMessageMapper.insert(message); return message; From b709af11a138b9ebadc0877980a38071304ebe6a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 21:00:34 +0800 Subject: [PATCH 258/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E8=81=8A=E5=A4=A9=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=BF=94=E5=9B=9E=E6=97=B6=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20segments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/chat/AiChatMessageController.java | 51 +++++++++++++++++-- .../chat/vo/message/AiChatMessageRespVO.java | 25 +++++++++ .../knowledge/AiKnowledgeController.java | 1 - .../dal/dataobject/chat/AiChatMessageDO.java | 18 +++---- .../chat/AiChatMessageServiceImpl.java | 2 +- .../AiKnowledgeDocumentServiceImpl.java | 5 +- .../knowledge/AiKnowledgeSegmentService.java | 22 ++++++++ .../AiKnowledgeSegmentServiceImpl.java | 15 ++++-- 8 files changed, 116 insertions(+), 23 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java index 43ae9a40db..b4fa8ab88c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java @@ -12,9 +12,13 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -32,7 +36,7 @@ import java.util.List; import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - 聊天消息") @@ -47,6 +51,10 @@ public class AiChatMessageController { private AiChatConversationService chatConversationService; @Resource private AiChatRoleService chatRoleService; + @Resource + private AiKnowledgeSegmentService knowledgeSegmentService; + @Resource + private AiKnowledgeDocumentService knowledgeDocumentService; @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢") @PostMapping("/send") @@ -56,7 +64,8 @@ public class AiChatMessageController { @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) { + public Flux> sendChatMessageStream( + @Valid @RequestBody AiChatMessageSendReqVO sendReqVO) { return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); } @@ -69,8 +78,38 @@ public class AiChatMessageController { if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { return success(Collections.emptyList()); } + // 1. 获取消息列表 List messageList = chatMessageService.getChatMessageListByConversationId(conversationId); - return success(BeanUtils.toBean(messageList, AiChatMessageRespVO.class)); + if (CollUtil.isEmpty(messageList)) { + return success(Collections.emptyList()); + } + + // 2. 拼接数据,主要是知识库段落信息 + Map segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList, + message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream())); + Map documentMap = knowledgeDocumentService.getKnowledgeDocumentMap( + convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId)); + List messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class); + for (int i = 0; i < messageList.size(); i++) { + AiChatMessageDO message = messageList.get(i); + if (CollUtil.isEmpty(message.getSegmentIds())) { + continue; + } + // 设置知识库段落信息 + messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> { + AiKnowledgeSegmentDO segment = segmentMap.get(segmentId); + if (segment == null) { + return null; + } + AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId()); + if (document == null) { + return null; + } + return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent()) + .setDocumentId(segment.getDocumentId()).setDocumentName(document.getName()); + })); + } + return success(messageVOList); } @Operation(summary = "删除消息") @@ -84,7 +123,8 @@ public class AiChatMessageController { @Operation(summary = "删除指定对话的消息") @DeleteMapping("/delete-by-conversation-id") @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024") - public CommonResult deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) { + public CommonResult deleteChatMessageByConversationId( + @RequestParam("conversationId") Long conversationId) { chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId()); return success(true); } @@ -103,7 +143,8 @@ public class AiChatMessageController { Map roleMap = chatRoleService.getChatRoleMap( convertSet(pageResult.getList(), AiChatMessageDO::getRoleId)); return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class, - respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName())))); + respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), + role -> respVO.setRoleName(role.getName())))); } @Operation(summary = "管理员删除消息") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java index 9b358df6f2..5d44e4f967 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - AI 聊天消息 Response VO") @Data @@ -39,6 +40,12 @@ public class AiChatMessageRespVO { @Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") private Boolean useContext; + @Schema(description = "知识库段落编号数组", example = "[1,2,3]") + private List segmentIds; + + @Schema(description = "知识库段落数组") + private List segments; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51") private LocalDateTime createTime; @@ -47,4 +54,22 @@ public class AiChatMessageRespVO { @Schema(description = "角色名字", example = "小黄") private String roleName; + @Schema(description = "知识库段落", example = "Java 开发手册") + @Data + public static class KnowledgeSegment { + + @Schema(description = "段落编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册") + private String content; + + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790") + private Long documentId; + + @Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册") + private String documentName; + + } + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index 39fb8d9430..7dd2e1647e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -23,7 +23,6 @@ import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -// TODO @芋艿:增加权限标识 @Tag(name = "管理后台 - AI 知识库") @RestController @RequestMapping("/ai/knowledge") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java index d121d85785..94f764c85e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java @@ -20,7 +20,7 @@ import java.util.List; * @since 2024/4/14 17:35 * @since 2024/4/14 17:35 */ -@TableName("ai_chat_message") +@TableName(value = "ai_chat_message", autoResultMap = true) @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data @EqualsAndHashCode(callSuper = true) @@ -71,14 +71,6 @@ public class AiChatMessageDO extends BaseDO { */ private Long roleId; - /** - * 知识库段落编号数组 - * - * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段 - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List segmentIds; - /** * 模型标志 * @@ -102,4 +94,12 @@ public class AiChatMessageDO extends BaseDO { */ private Boolean useContext; + /** + * 知识库段落编号数组 + * + * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List segmentIds; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index b95f7cd214..cd6c39d405 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -212,7 +212,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { // 1.4 知识库,通过 UserMessage 实现 if (CollUtil.isNotEmpty(knowledgeSegments)) { String reference = knowledgeSegments.stream() - .map(segment -> "\n" + segment.getContent() + "") + .map(segment -> "" + segment.getContent() + "") .collect(Collectors.joining("\n\n")); chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference))); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java index 48dd78dbb9..2d78f94f34 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java @@ -26,6 +26,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -205,9 +206,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @Override public List getKnowledgeDocumentList(Collection ids) { if (CollUtil.isEmpty(ids)) { - return new ArrayList<>(); + return Collections.emptyList(); } - return knowledgeDocumentMapper.selectByIds(ids); + return knowledgeDocumentMapper.selectBatchIds(ids); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 15ab941fe8..32272abdb8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -10,7 +10,11 @@ import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchR import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; import org.springframework.scheduling.annotation.Async; +import java.util.Collection; import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * AI 知识库段落 Service 接口 @@ -27,6 +31,24 @@ public interface AiKnowledgeSegmentService { */ AiKnowledgeSegmentDO getKnowledgeSegment(Long id); + /** + * 获取知识库段落列表 + * + * @param ids 段落编号列表 + * @return 段落列表 + */ + List getKnowledgeSegmentList(Collection ids); + + /** + * 获取知识库段落 Map + * + * @param ids 段落编号列表 + * @return 段落 Map + */ + default Map getKnowledgeSegmentMap(Collection ids) { + return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId); + } + /** * 获取段落分页 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index efdd86203f..94b735b3c5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -30,10 +30,7 @@ import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -322,7 +319,15 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @Override public AiKnowledgeSegmentDO getKnowledgeSegment(Long id) { - return validateKnowledgeSegmentExists(id); + return segmentMapper.selectById(id); + } + + @Override + public List getKnowledgeSegmentList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return segmentMapper.selectBatchIds(ids); } } From 32e1ef4da81848179bd5f7e6c098f1f0bc4c0ddc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Mar 2025 21:19:41 +0800 Subject: [PATCH 259/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E8=81=8A=E5=A4=A9=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=BF=94=E5=9B=9E=E6=97=B6=EF=BC=8C=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20segments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/message/AiChatMessageSendRespVO.java | 7 +++ .../chat/AiChatMessageServiceImpl.java | 50 +++++++++++++------ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java index 58ba056595..245a19f7cb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - AI 聊天消息发送 Response VO") @Data @@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO { @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") private String content; + @Schema(description = "知识库段落编号数组", example = "[1,2,3]") + private List segmentIds; + + @Schema(description = "知识库段落数组") + private List segments; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index cd6c39d405..88f7144336 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -10,14 +10,17 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService; import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO; import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; @@ -76,6 +79,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { private AiModelService modalService; @Resource private AiKnowledgeSegmentService knowledgeSegmentService; + @Resource + private AiKnowledgeDocumentService knowledgeDocumentService; @Transactional(rollbackFor = Exception.class) public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { @@ -108,18 +113,23 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO); ChatResponse chatResponse = chatModel.call(prompt); - // 3.3 段式返回 + // 3.3 更新响应内容 String newContent = chatResponse.getResult().getOutput().getText(); chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent)); + // 3.4 响应结果 + List segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, + segment -> { + AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId()); + segment.setDocumentName(document != null ? document.getName() : null); + }); return new AiChatMessageSendRespVO() .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) - .setContent(newContent)); + .setContent(newContent).setSegments(segments)); } @Override - public Flux> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, - Long userId) { + public Flux> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { // 1.1 校验对话存在 AiChatConversationDO conversation = chatConversationService .validateChatConversationExists(sendReqVO.getConversationId()); @@ -132,8 +142,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { StreamingChatModel chatModel = modalService.getChatModel(model.getId()); // 2. 知识库找回 - List knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), - conversation); + List knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation); // 3. 插入 user 发送消息 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, @@ -152,14 +161,23 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { // 4.3 流式返回 StringBuffer contentBuffer = new StringBuffer(); return streamResponse.map(chunk -> { + // 处理知识库的返回,只有首次才有 + List segments = null; + if (StrUtil.isEmpty(contentBuffer)) { + segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, + segment -> TenantUtils.executeIgnore(() -> { + AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId()); + segment.setDocumentName(document != null ? document.getName() : null); + })); + } + // 响应结果 String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null; newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况 contentBuffer.append(newContent); - // 响应结果 return success(new AiChatMessageSendRespVO() .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) - .setContent(newContent))); + .setContent(newContent).setSegments(segments))); }).doOnComplete(() -> { // 忽略租户,因为 Flux 异步无法透传租户 TenantUtils.executeIgnore(() -> chatMessageMapper.updateById( @@ -173,7 +191,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private List recallKnowledgeSegment(String content, - AiChatConversationDO conversation) { + AiChatConversationDO conversation) { // 1. 查询聊天角色 if (conversation == null || conversation.getRoleId() == null) { return Collections.emptyList(); @@ -193,8 +211,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private Prompt buildPrompt(AiChatConversationDO conversation, List messages, - List knowledgeSegments, - AiModelDO model, AiChatMessageSendReqVO sendReqVO) { + List knowledgeSegments, + AiModelDO model, AiChatMessageSendReqVO sendReqVO) { List chatMessages = new ArrayList<>(); // 1.1 System Context 角色设定 if (StrUtil.isNotBlank(conversation.getSystemMessage())) { @@ -235,8 +253,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { * @return 消息上下文 */ private List filterContextMessages(List messages, - AiChatConversationDO conversation, - AiChatMessageSendReqVO sendReqVO) { + AiChatConversationDO conversation, + AiChatMessageSendReqVO sendReqVO) { if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) { return Collections.emptyList(); } @@ -265,9 +283,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, - AiModelDO model, Long userId, Long roleId, - MessageType messageType, String content, Boolean useContext, - List knowledgeSegments) { + AiModelDO model, Long userId, Long roleId, + MessageType messageType, String content, Boolean useContext, + List knowledgeSegments) { AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) .setType(messageType.getValue()).setContent(content).setUseContext(useContext) From d7e801c438d3e4c2e7f4e34cec224dd23b4a3efc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 10 Mar 2025 09:12:24 +0800 Subject: [PATCH 260/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E7=9F=A5=E8=AF=86=E5=BA=93?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=9A=84=E6=A8=A1=E5=9E=8B=E5=8F=98=E5=8C=96?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E8=A7=A6=E5=8F=91=E9=87=8D=E7=B4=A2=E5=BC=95?= =?UTF-8?q?=EF=BC=88=E5=BC=82=E6=AD=A5=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledge/AiKnowledgeSegmentMapper.java | 7 +++++- .../knowledge/AiKnowledgeSegmentService.java | 17 +++++++++++++ .../AiKnowledgeSegmentServiceImpl.java | 24 +++++++++++++++++++ .../knowledge/AiKnowledgeServiceImpl.java | 12 ++++++++-- .../ai/service/model/AiModelServiceImpl.java | 6 ++--- 5 files changed, 60 insertions(+), 6 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java index a2a4e15b6d..00bacd9665 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java @@ -42,6 +42,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) { + return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId, + AiKnowledgeSegmentDO::getStatus, status); + } + default List selectProcessList(Collection documentIds) { MPJLambdaWrapper wrapper = new MPJLambdaWrapperX() .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId) @@ -54,7 +59,7 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX ids) { - update( new LambdaUpdateWrapper() + update(new LambdaUpdateWrapper() .setSql(" retrieval_count = retrieval_count + 1") .in(AiKnowledgeSegmentDO::getId, ids)); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java index 32272abdb8..54f7217055 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java @@ -98,6 +98,23 @@ public interface AiKnowledgeSegmentService { */ void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); + /** + * 重新索引知识库下的所有文档段落 + * + * @param knowledgeId 知识库编号 + */ + void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId); + + /** + * 【异步】重新索引知识库下的所有文档段落 + * + * @param knowledgeId 知识库编号 + */ + @Async + default void reindexByKnowledgeIdAsync(Long knowledgeId) { + reindexKnowledgeSegmentByKnowledgeId(knowledgeId); + } + /** * 根据文档编号删除段落 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java index 94b735b3c5..20f881cf13 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java @@ -158,6 +158,30 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService } } + @Override + public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) { + // 1.1 校验知识库存在 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId); + // 1.2 获取知识库向量实例 + VectorStore vectorStore = getVectorStoreById(knowledge); + + // 2.1 查询知识库下的所有启用状态的段落 + List segments = segmentMapper.selectListByKnowledgeIdAndStatus( + knowledgeId, CommonStatusEnum.ENABLE.getStatus()); + if (CollUtil.isEmpty(segments)) { + return; + } + // 2.2 遍历所有段落,重新索引 + for (AiKnowledgeSegmentDO segment : segments) { + // 删除旧的向量 + deleteVectorStore(vectorStore, segment); + // 重新创建向量 + writeVectorStore(vectorStore, segment, new Document(segment.getContent())); + } + log.info("[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成,共处理 {} 个段落]", + knowledgeId, segments.size()); + } + private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) { // 1. 向量存储 // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java index 448a0d6ba1..59afd7d7bc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java @@ -1,10 +1,13 @@ package cn.iocoder.yudao.module.ai.service.knowledge; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; import cn.iocoder.yudao.module.ai.service.model.AiModelService; @@ -31,6 +34,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @Resource private AiModelService modelService; + @Resource + private AiKnowledgeSegmentService knowledgeSegmentService; @Override public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) { @@ -47,7 +52,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @Override public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) { // 1.1 校验知识库存在 - validateKnowledgeExists(updateReqVO.getId()); + AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId()); // 1.2 校验模型配置 AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId()); @@ -56,7 +61,10 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { .setEmbeddingModel(model.getModel()); knowledgeMapper.updateById(updateObj); - // TODO @芋艿:如果模型变化,需要 reindex 所有的文档 + // 3. 如果模型变化,需要 reindex 所有的文档 + if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) { + knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId()); + } } @Override diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index b73567a1c5..b7e0c99d0f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -16,8 +16,8 @@ import jakarta.annotation.Resource; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; +import org.springframework.ai.vectorstore.SimpleVectorStore; import org.springframework.ai.vectorstore.VectorStore; -import org.springframework.ai.vectorstore.milvus.MilvusVectorStore; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -162,10 +162,10 @@ public class AiModelServiceImpl implements AiModelService { platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); // 创建或获取 VectorStore 对象 - // return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); + return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); // return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); // return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); - return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); +// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); } } \ No newline at end of file From 3a3607e1cbd63999422f6e732e395446ec8dbf4e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 11 Mar 2025 07:49:54 +0800 Subject: [PATCH 261/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20ZhiPuAiEmbedd?= =?UTF-8?q?ingModel=20=E5=90=91=E9=87=8F=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../knowledge/AiKnowledgeController.java | 1 - .../ai/service/model/AiModelServiceImpl.java | 6 ++--- .../ai/core/factory/AiModelFactoryImpl.java | 22 ++++++++++++++----- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java index 7dd2e1647e..c6e31f0e8d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java @@ -36,7 +36,6 @@ public class AiKnowledgeController { @Operation(summary = "获取知识库分页") @PreAuthorize("@ss.hasPermission('ai:knowledge:query')") public CommonResult> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) { - PageResult pageResult = knowledgeService.getKnowledgePage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class)); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java index b7e0c99d0f..b0e9e97172 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java @@ -163,9 +163,9 @@ public class AiModelServiceImpl implements AiModelService { // 创建或获取 VectorStore 对象 return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); - // return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); - // return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); -// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); +// return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); +// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); +// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); } } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 7953157009..180886b99c 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -77,6 +77,7 @@ import org.springframework.ai.vectorstore.observation.VectorStoreObservationConv import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import org.springframework.ai.vectorstore.redis.RedisVectorStore; import org.springframework.ai.zhipuai.ZhiPuAiChatModel; +import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel; import org.springframework.ai.zhipuai.ZhiPuAiImageModel; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; @@ -231,7 +232,9 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildTongYiEmbeddingModel(apiKey, model); case OLLAMA: return buildOllamaEmbeddingModel(url, model); - // TODO @芋艿:各个平台的向量化能力; + // TODO @芋艿:yiyan + case ZHI_PU: + return buildZhiPuEmbeddingModel(apiKey, url, model); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -240,8 +243,8 @@ public class AiModelFactoryImpl implements AiModelFactory { @Override public VectorStore getOrCreateVectorStore(Class type, - EmbeddingModel embeddingModel, - Map> metadataFields) { + EmbeddingModel embeddingModel, + Map> metadataFields) { String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); return Singleton.get(cacheKey, (Func0) () -> { if (type == SimpleVectorStore.class) { @@ -425,17 +428,24 @@ public class AiModelFactoryImpl implements AiModelFactory { // ========== 各种创建 EmbeddingModel 的方法 ========== - // TODO @芋艿:需要测试下 /** * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeEmbeddingModel 方法 */ private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { DashScopeApi dashScopeApi = new DashScopeApi(apiKey); - DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model) - .build(); + DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); } + /** + * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 + */ + private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { + url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); + ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi(url, apiKey); + return new ZhiPuAiEmbeddingModel(zhiPuAiApi); + } + private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { OllamaApi ollamaApi = new OllamaApi(url); OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build(); From 20eb3013f273d018f84715b64bf10c30d022b8e4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 11 Mar 2025 07:57:14 +0800 Subject: [PATCH 262/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20QianFanEmbedd?= =?UTF-8?q?ingModel=20=E5=90=91=E9=87=8F=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/core/factory/AiModelFactoryImpl.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 180886b99c..4bd519555c 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -64,6 +64,8 @@ import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiImageApi; import org.springframework.ai.openai.api.common.OpenAiApiConstants; import org.springframework.ai.qianfan.QianFanChatModel; +import org.springframework.ai.qianfan.QianFanEmbeddingModel; +import org.springframework.ai.qianfan.QianFanEmbeddingOptions; import org.springframework.ai.qianfan.QianFanImageModel; import org.springframework.ai.qianfan.api.QianFanApi; import org.springframework.ai.qianfan.api.QianFanImageApi; @@ -78,6 +80,7 @@ import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import org.springframework.ai.vectorstore.redis.RedisVectorStore; import org.springframework.ai.zhipuai.ZhiPuAiChatModel; import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel; +import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions; import org.springframework.ai.zhipuai.ZhiPuAiImageModel; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; @@ -230,11 +233,16 @@ public class AiModelFactoryImpl implements AiModelFactory { switch (platform) { case TONG_YI: return buildTongYiEmbeddingModel(apiKey, model); - case OLLAMA: - return buildOllamaEmbeddingModel(url, model); - // TODO @芋艿:yiyan + case YI_YAN: + return buildYiYanEmbeddingModel(apiKey, model); case ZHI_PU: return buildZhiPuEmbeddingModel(apiKey, url, model); +// case OPENAI: +// return buildOpenAiChatModel(apiKey, url); +// case AZURE_OPENAI: +// return buildAzureOpenAiChatModel(apiKey, url); + case OLLAMA: + return buildOllamaEmbeddingModel(url, model); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } @@ -443,7 +451,21 @@ public class AiModelFactoryImpl implements AiModelFactory { private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi(url, apiKey); - return new ZhiPuAiEmbeddingModel(zhiPuAiApi); + ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build(); + return new ZhiPuAiEmbeddingModel(zhiPuAiApi, MetadataMode.EMBED, zhiPuAiEmbeddingOptions); + } + + /** + * 可参考 {@link QianFanAutoConfiguration} 的 qianFanEmbeddingModel 方法 + */ + private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) { + List keys = StrUtil.split(key, '|'); + Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); + String appKey = keys.get(0); + String secretKey = keys.get(1); + QianFanApi qianFanApi = new QianFanApi(appKey, secretKey); + QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build(); + return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions); } private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { From b9e8495712de03b9c7e215b43f772c0c9b85ef0a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 11 Mar 2025 09:42:24 +0800 Subject: [PATCH 263/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20AzureOpenAiEm?= =?UTF-8?q?beddingModel=E3=80=81OpenAiEmbeddingModel=20=E5=90=91=E9=87=8F?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/core/factory/AiModelFactoryImpl.java | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 4bd519555c..b588027cde 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -35,6 +35,7 @@ import lombok.SneakyThrows; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties; +import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiEmbeddingProperties; import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; @@ -49,6 +50,7 @@ import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStorePr import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; +import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.document.MetadataMode; import org.springframework.ai.embedding.BatchingStrategy; @@ -59,6 +61,8 @@ import org.springframework.ai.ollama.OllamaEmbeddingModel; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiEmbeddingModel; +import org.springframework.ai.openai.OpenAiEmbeddingOptions; import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.api.OpenAiApi; import org.springframework.ai.openai.api.OpenAiImageApi; @@ -227,6 +231,7 @@ public class AiModelFactoryImpl implements AiModelFactory { } @Override + @SuppressWarnings("EnhancedSwitchMigration") public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) { String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model); return Singleton.get(cacheKey, (Func0) () -> { @@ -237,10 +242,10 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildYiYanEmbeddingModel(apiKey, model); case ZHI_PU: return buildZhiPuEmbeddingModel(apiKey, url, model); -// case OPENAI: -// return buildOpenAiChatModel(apiKey, url); -// case AZURE_OPENAI: -// return buildAzureOpenAiChatModel(apiKey, url); + case OPENAI: + return buildOpenAiEmbeddingModel(apiKey, url, model); + case AZURE_OPENAI: + return buildAzureOpenAiEmbeddingModel(apiKey, url, model); case OLLAMA: return buildOllamaEmbeddingModel(url, model); default: @@ -474,6 +479,33 @@ public class AiModelFactoryImpl implements AiModelFactory { return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).defaultOptions(ollamaOptions).build(); } + /** + * 可参考 {@link OpenAiAutoConfiguration} 的 openAiEmbeddingModel 方法 + */ + private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) { + url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); + OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); + OpenAiEmbeddingOptions openAiEmbeddingProperties = OpenAiEmbeddingOptions.builder().model(model).build(); + return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties); + } + + // TODO @芋艿:手头暂时没密钥,使用建议再测试下 + /** + * 可参考 {@link AzureOpenAiAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 + */ + private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) { + AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration(); + // 创建 OpenAIClient 对象 + AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties(); + connectionProperties.setApiKey(apiKey); + connectionProperties.setEndpoint(url); + OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null); + // 获取 AzureOpenAiChatProperties 对象 + AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class); + return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClient, embeddingProperties, + null, null); + } + // ========== 各种创建 VectorStore 的方法 ========== /** From 99d5e7c5094875bd379f7cd98d05d387677eccae Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 11 Mar 2025 12:53:34 +0800 Subject: [PATCH 264/309] =?UTF-8?q?=E3=80=90=E4=BE=9D=E8=B5=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E3=80=91AI=EF=BC=9Aspring-ai-alibaba-starter=20?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0=E6=9C=80=E6=96=B0=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E6=B5=81=E5=BC=8F=E8=BF=94=E5=9B=9E=E6=8A=A5=E9=94=99?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml | 2 +- .../java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java | 3 +-- .../iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java | 1 + .../yudao/framework/ai/image/TongYiImagesModelTest.java | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index 2d1a0af3aa..5a10c137f9 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -49,7 +49,7 @@ com.alibaba.cloud.ai spring-ai-alibaba-starter - 1.0.0-M5.1 + ${spring-ai.version}.1 diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index a96d305492..cb968c3ffc 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -22,8 +22,7 @@ public class AiUtils { // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: - // TODO @芋艿:tongyi 暂时没 maxTokens 选项 - return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).build(); + return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens).build(); case YI_YAN: return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java index c632acf20f..b51d556a36 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/TongYiChatModelTests.java @@ -28,6 +28,7 @@ public class TongYiChatModelTests { .withModel("qwen1.5-72b-chat") // 模型 // .withModel("deepseek-r1") // 模型(deepseek-r1) // .withModel("deepseek-v3") // 模型(deepseek-v3) +// .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) .build()); @Test diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java index cc62f02fb6..ad4faaa46b 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/TongYiImagesModelTest.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.ai.image; import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; -import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.image.ImageOptions; @@ -25,7 +24,7 @@ public class TongYiImagesModelTest { public void imageCallTest() { // 准备参数 ImageOptions options = DashScopeImageOptions.builder() - .withModel(ImageSynthesis.Models.WANX_V1) + .withModel("wanx-v1") .withHeight(256).withWidth(256) .build(); ImagePrompt prompt = new ImagePrompt("中国长城!", options); From 07fe6167e09d5b4e4d5707a4d313b5b27f4cc3f2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 11 Mar 2025 13:20:59 +0800 Subject: [PATCH 265/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=8E=A5=E5=85=A5=20minimax=20?= =?UTF-8?q?=E5=92=8C=20moonshot=20=E6=9C=88=E4=B9=8B=E6=9A=97=E7=81=AD?= =?UTF-8?q?=EF=BC=88kimi=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-spring-boot-starter-ai/pom.xml | 29 ++++++--- .../ai/core/enums/AiPlatformEnum.java | 2 + .../ai/core/factory/AiModelFactoryImpl.java | 59 +++++++++++++++--- .../yudao/framework/ai/core/util/AiUtils.java | 6 ++ .../ai/chat/MiniMaxChatModelTests.java | 62 +++++++++++++++++++ .../ai/chat/MoonshotChatModelTests.java | 62 +++++++++++++++++++ .../src/main/resources/application.yaml | 4 ++ 7 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MiniMaxChatModelTests.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MoonshotChatModelTests.java diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml index 5a10c137f9..f37f3709c6 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -14,7 +14,6 @@ ${project.artifactId} AI 大模型拓展,接入国内外大模型 - org.springframework.ai 1.0.0-M6 @@ -26,22 +25,22 @@ - ${spring-ai.groupId} + org.springframework.ai spring-ai-openai-spring-boot-starter ${spring-ai.version} - ${spring-ai.groupId} + org.springframework.ai spring-ai-azure-openai-spring-boot-starter ${spring-ai.version} - ${spring-ai.groupId} + org.springframework.ai spring-ai-ollama-spring-boot-starter ${spring-ai.version} - ${spring-ai.groupId} + org.springframework.ai spring-ai-stability-ai-spring-boot-starter ${spring-ai.version} @@ -53,28 +52,38 @@ - ${spring-ai.groupId} + org.springframework.ai spring-ai-qianfan-spring-boot-starter ${spring-ai.version} - ${spring-ai.groupId} + org.springframework.ai spring-ai-zhipuai-spring-boot-starter ${spring-ai.version} + + org.springframework.ai + spring-ai-minimax-spring-boot-starter + ${spring-ai.version} + + + org.springframework.ai + spring-ai-moonshot-spring-boot-starter + ${spring-ai.version} + - ${spring-ai.groupId} + org.springframework.ai spring-ai-qdrant-store ${spring-ai.version} - ${spring-ai.groupId} + org.springframework.ai spring-ai-redis-store ${spring-ai.version} @@ -92,7 +101,7 @@ - ${spring-ai.groupId} + org.springframework.ai spring-ai-tika-document-reader ${spring-ai.version} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java index a154db8c88..5a8a5c4539 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/enums/AiPlatformEnum.java @@ -25,6 +25,8 @@ public enum AiPlatformEnum implements ArrayValuable { DOU_BAO("DouBao", "豆包"), // 字节 HUN_YUAN("HunYuan", "混元"), // 腾讯 SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动 + MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技 + MOONSHOT("Moonshot", "月之暗灭"), // KIMI // ========== 国外平台 ========== diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index b588027cde..dbbbbe2241 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -36,6 +36,8 @@ import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfigur import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties; import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiEmbeddingProperties; +import org.springframework.ai.autoconfigure.minimax.MiniMaxAutoConfiguration; +import org.springframework.ai.autoconfigure.moonshot.MoonshotAutoConfiguration; import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; @@ -48,7 +50,6 @@ import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStore import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration; import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; -import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; import org.springframework.ai.chat.model.ChatModel; @@ -56,6 +57,12 @@ import org.springframework.ai.document.MetadataMode; import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; +import org.springframework.ai.minimax.MiniMaxChatModel; +import org.springframework.ai.minimax.MiniMaxEmbeddingModel; +import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; +import org.springframework.ai.minimax.api.MiniMaxApi; +import org.springframework.ai.moonshot.MoonshotChatModel; +import org.springframework.ai.moonshot.api.MoonshotApi; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaEmbeddingModel; import org.springframework.ai.ollama.api.OllamaApi; @@ -130,6 +137,10 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildSiliconFlowChatModel(apiKey); case ZHI_PU: return buildZhiPuChatModel(apiKey, url); + case MINI_MAX: + return buildMiniMaxChatModel(apiKey, url); + case MOONSHOT: + return buildMoonshotChatModel(apiKey, url); case XING_HUO: return buildXingHuoChatModel(apiKey); case OPENAI: @@ -162,6 +173,10 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(SiliconFlowChatModel.class); case ZHI_PU: return SpringUtil.getBean(ZhiPuAiChatModel.class); + case MINI_MAX: + return SpringUtil.getBean(MiniMaxChatModel.class); + case MOONSHOT: + return SpringUtil.getBean(MoonshotChatModel.class); case XING_HUO: return SpringUtil.getBean(XingHuoChatModel.class); case OPENAI: @@ -242,6 +257,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildYiYanEmbeddingModel(apiKey, model); case ZHI_PU: return buildZhiPuEmbeddingModel(apiKey, url, model); + case MINI_MAX: + return buildMiniMaxEmbeddingModel(apiKey, url, model); case OPENAI: return buildOpenAiEmbeddingModel(apiKey, url, model); case AZURE_OPENAI: @@ -365,8 +382,8 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiChatModel 方法 */ private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { - url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); - ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi(url, apiKey); + ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) + : new ZhiPuAiApi(url, apiKey); return new ZhiPuAiChatModel(zhiPuAiApi); } @@ -374,11 +391,29 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiImageModel 方法 */ private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { - url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); - ZhiPuAiImageApi zhiPuAiApi = new ZhiPuAiImageApi(url, apiKey, RestClient.builder()); + ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey) + : new ZhiPuAiImageApi(url, apiKey, RestClient.builder()); return new ZhiPuAiImageModel(zhiPuAiApi); } + /** + * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxChatModel 方法 + */ + private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { + MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) + : new MiniMaxApi(url, apiKey); + return new MiniMaxChatModel(miniMaxApi); + } + + /** + * 可参考 {@link MoonshotAutoConfiguration} 的 moonshotChatModel 方法 + */ + private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { + MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) + : new MoonshotApi(url, apiKey); + return new MoonshotChatModel(moonshotApi); + } + /** * 可参考 {@link YudaoAiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)} */ @@ -454,12 +489,22 @@ public class AiModelFactoryImpl implements AiModelFactory { * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 */ private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { - url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL); - ZhiPuAiApi zhiPuAiApi = new ZhiPuAiApi(url, apiKey); + ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) + : new ZhiPuAiApi(url, apiKey); ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build(); return new ZhiPuAiEmbeddingModel(zhiPuAiApi, MetadataMode.EMBED, zhiPuAiEmbeddingOptions); } + /** + * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxEmbeddingModel 方法 + */ + private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) { + MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey) + : new MiniMaxApi(url, apiKey); + MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build(); + return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions); + } + /** * 可参考 {@link QianFanAutoConfiguration} 的 qianFanEmbeddingModel 方法 */ diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index cb968c3ffc..c3a87358cd 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -6,6 +6,8 @@ import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; import org.springframework.ai.chat.messages.*; import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.minimax.MiniMaxChatOptions; +import org.springframework.ai.moonshot.MoonshotChatOptions; import org.springframework.ai.ollama.api.OllamaOptions; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.qianfan.QianFanChatOptions; @@ -27,6 +29,10 @@ public class AiUtils { return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); + case MINI_MAX: + return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); + case MOONSHOT: + return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case OPENAI: case DEEP_SEEK: // 复用 OpenAI 客户端 case DOU_BAO: // 复用 OpenAI 客户端 diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MiniMaxChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MiniMaxChatModelTests.java new file mode 100644 index 0000000000..80b60aea94 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MiniMaxChatModelTests.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.framework.ai.chat; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.minimax.MiniMaxChatModel; +import org.springframework.ai.minimax.MiniMaxChatOptions; +import org.springframework.ai.minimax.api.MiniMaxApi; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link MiniMaxChatModel} 的集成测试 + * + * @author 芋道源码 + */ +public class MiniMaxChatModelTests { + + private final MiniMaxChatModel chatModel = new MiniMaxChatModel( + new MiniMaxApi("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLnjovmlofmlowiLCJVc2VyTmFtZSI6IueOi-aWh-aWjCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxODk3Mjg3MjQ5NDU2ODA4MzQ2IiwiUGhvbmUiOiIxNTYwMTY5MTM5OSIsIkdyb3VwSUQiOiIxODk3Mjg3MjQ5NDQ4NDE5NzM4IiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDMtMTEgMTI6NTI6MDIiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.aAuB7gWW_oA4IYhh-CF7c9MfWWxKN49B_HK-DYjXaDwwffhiG-H1571z1WQhp9QytWG-DqgLejneeSxkiq1wQIe3FsEP2wz4BmGBct31LehbJu8ehLxg_vg75Uod1nFAHbm5mZz6JSVLNIlSo87Xr3UtSzJhAXlapEkcqlA4YOzOpKrZ8l5_OJPTORTCmHWZYgJcRS-faNiH62ZnUEHUozesTFhubJHo5GfJCw_edlnmfSUocERV1BjWvenhZ9My-aYXNktcW9WaSj9l6gayV7A0Ium_PL55T9ln1PcI8gayiVUKJGJDoqNyF1AF9_aF9NOKtTnQzwNqnZdlTYH6hw"), // 密钥 + MiniMaxChatOptions.builder() + .model(MiniMaxApi.ChatModel.ABAB_6_5_G_Chat.getValue()) // 模型 + .build()); + @Test + @Disabled + public void testCall() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + ChatResponse response = chatModel.call(new Prompt(messages)); + // 打印结果 + System.out.println(response); + System.out.println(response.getResult().getOutput()); + } + + @Test + @Disabled + public void testStream() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + Flux flux = chatModel.stream(new Prompt(messages)); + // 打印结果 + flux.doOnNext(response -> { +// System.out.println(response); + System.out.println(response.getResult().getOutput()); + }).then().block(); + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MoonshotChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MoonshotChatModelTests.java new file mode 100644 index 0000000000..e3f644a6f7 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/MoonshotChatModelTests.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.framework.ai.chat; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.moonshot.MoonshotChatModel; +import org.springframework.ai.moonshot.MoonshotChatOptions; +import org.springframework.ai.moonshot.api.MoonshotApi; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link org.springframework.ai.moonshot.MoonshotChatModel} 的集成测试 + * + * @author 芋道源码 + */ +public class MoonshotChatModelTests { + + private final MoonshotChatModel chatModel = new MoonshotChatModel( + new MoonshotApi("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA"), // 密钥 + MoonshotChatOptions.builder() + .model("moonshot-v1-8k") // 模型 + .build()); + @Test + @Disabled + public void testCall() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + ChatResponse response = chatModel.call(new Prompt(messages)); + // 打印结果 + System.out.println(response); + System.out.println(response.getResult().getOutput()); + } + + @Test + @Disabled + public void testStream() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + Flux flux = chatModel.stream(new Prompt(messages)); + // 打印结果 + flux.doOnNext(response -> { +// System.out.println(response); + System.out.println(response.getResult().getOutput()); + }).then().block(); + } + +} diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 419fab471b..8c906e3899 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -184,6 +184,10 @@ spring: api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx dashscope: # 通义千问 api-key: sk-71800982914041848008480000000000 + minimax: # Minimax:https://www.minimaxi.com/ + api-key: xxxx + moonshot: # 月之暗灭(KIMI) + api-key: sk-abc yudao: ai: From f7ab30c50a7b8a899dacaab08f94cbf086b9f1f2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 12 Mar 2025 21:50:07 +0800 Subject: [PATCH 266/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20Dify=E3=80=81?= =?UTF-8?q?FastGPT=20=E7=9A=84=E6=8E=A5=E5=85=A5=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/ai/chat/DifyChatModelTests.java | 63 +++++++++++++++++++ .../ai/chat/FastGPTChatModelTests.java | 63 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DifyChatModelTests.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/FastGPTChatModelTests.java diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DifyChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DifyChatModelTests.java new file mode 100644 index 0000000000..8b02346bbc --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/DifyChatModelTests.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.framework.ai.chat; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.api.OpenAiApi; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.List; + +/** + * 基于 {@link OpenAiChatModel} 集成 Dify 测试 + * + * @author 芋道源码 + */ +public class DifyChatModelTests { + + private final OpenAiChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl("http://127.0.0.1:3000") + .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey + .build()) + .build(); + + @Test + @Disabled + public void testCall() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + ChatResponse response = chatModel.call(new Prompt(messages)); + // 打印结果 + System.out.println(response); + System.out.println(response.getResult().getOutput()); + } + + @Test + @Disabled + public void testStream() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + Flux flux = chatModel.stream(new Prompt(messages)); + // 打印结果 + flux.doOnNext(response -> { +// System.out.println(response); + System.out.println(response.getResult().getOutput()); + }).then().block(); + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/FastGPTChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/FastGPTChatModelTests.java new file mode 100644 index 0000000000..b58807b793 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/FastGPTChatModelTests.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.framework.ai.chat; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.ai.chat.messages.Message; +import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.UserMessage; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.api.OpenAiApi; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.List; + +/** + * 基于 {@link OpenAiChatModel} 集成 FastGPT 测试 + * + * @author 芋道源码 + */ +public class FastGPTChatModelTests { + + private final OpenAiChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl("https://cloud.fastgpt.cn/api") + .apiKey("fastgpt-aqcc61kFtF8CeaglnGAfQOCIDWwjGdJVJHv6hIlMo28otFlva2aZNK") // apiKey + .build()) + .build(); + + @Test + @Disabled + public void testCall() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + ChatResponse response = chatModel.call(new Prompt(messages)); + // 打印结果 + System.out.println(response); + System.out.println(response.getResult().getOutput()); + } + + @Test + @Disabled + public void testStream() { + // 准备参数 + List messages = new ArrayList<>(); + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); + messages.add(new UserMessage("1 + 1 = ?")); + + // 调用 + Flux flux = chatModel.stream(new Prompt(messages)); + // 打印结果 + flux.doOnNext(response -> { +// System.out.println(response); + System.out.println(response.getResult().getOutput()); + }).then().block(); + } + +} From 569d6514816e373c9fa2ec66295707a2b7a7c89e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 08:17:31 +0800 Subject: [PATCH 267/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT=EF=BC=9A=E5=A2=9E=E5=8A=A0=20device=20con?= =?UTF-8?q?fig=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../device/vo/device/IotDeviceRespVO.java | 3 +++ .../device/vo/device/IotDeviceSaveReqVO.java | 3 +++ .../dal/tdengine/IotDevicePropertyMapper.java | 1 - .../router/IotDeviceWebhookVertxHandler.java | 20 +++++++------------ yudao-server/pom.xml | 10 +++++----- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java index cc45c0280f..8404ca9224 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java @@ -83,6 +83,9 @@ public class IotDeviceRespVO { @ExcelProperty("认证类型(如一机一密、动态注册)") private String authType; + @Schema(description = "设备配置", example = "{\"abc\": \"efg\"}") + private String config; + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") private LocalDateTime createTime; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java index 7bf0f33ee9..b9ea9b99fa 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceSaveReqVO.java @@ -38,4 +38,7 @@ public class IotDeviceSaveReqVO { @Schema(description = "网关设备 ID", example = "16380") private Long gatewayId; + @Schema(description = "设备配置", example = "{\"abc\": \"efg\"}") + private String config; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java index 955b384780..37a72e4b02 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDevicePropertyMapper.java @@ -32,7 +32,6 @@ public interface IotDevicePropertyMapper { default void alterProductPropertySTable(String productKey, List oldFields, List newFields) { - // TODO @芋艿:需要处理 device_key,重新发布的时候 oldFields.removeIf(field -> StrUtil.equalsAny(field.getField(), TDengineTableField.FIELD_TS, "report_time", "device_key")); List addFields = newFields.stream().filter( // 新增的字段 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java index a2499826fd..31679fe056 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java @@ -17,9 +17,8 @@ import java.util.Collections; /** * IoT Emqx Webhook 事件处理的 Vert.x Handler - * EMQX - * Webhook + * + * EMQXWebhook * * @author haohao */ @@ -69,12 +68,11 @@ public class IotDeviceWebhookVertxHandler implements Handler { * @param username 用户名 */ private void handleClientConnected(String clientId, String username) { + // 解析产品标识和设备名称 if (StrUtil.isEmpty(username) || "undefined".equals(username)) { log.warn("[handleClientConnected][客户端连接事件,但用户名为空] clientId={}", clientId); return; } - - // 解析产品标识和设备名称 String[] parts = parseUsername(username); if (parts == null) { return; @@ -87,7 +85,6 @@ public class IotDeviceWebhookVertxHandler implements Handler { updateReqDTO.setState(IotDeviceStateEnum.ONLINE.getState()); updateReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); updateReqDTO.setReportTime(LocalDateTime.now()); - CommonResult result = deviceUpstreamApi.updateDeviceState(updateReqDTO); if (result.getCode() != 0 || !result.getData()) { log.error("[handleClientConnected][更新设备状态为在线失败] clientId={}, username={}, code={}, msg={}", @@ -104,12 +101,11 @@ public class IotDeviceWebhookVertxHandler implements Handler { * @param username 用户名 */ private void handleClientDisconnected(String clientId, String username) { + // 解析产品标识和设备名称 if (StrUtil.isEmpty(username) || "undefined".equals(username)) { log.warn("[handleClientDisconnected][客户端断开连接事件,但用户名为空] clientId={}", clientId); return; } - - // 解析产品标识和设备名称 String[] parts = parseUsername(username); if (parts == null) { return; @@ -122,7 +118,6 @@ public class IotDeviceWebhookVertxHandler implements Handler { offlineReqDTO.setState(IotDeviceStateEnum.OFFLINE.getState()); offlineReqDTO.setProcessId(IotPluginCommonUtils.getProcessId()); offlineReqDTO.setReportTime(LocalDateTime.now()); - CommonResult offlineResult = deviceUpstreamApi.updateDeviceState(offlineReqDTO); if (offlineResult.getCode() != 0 || !offlineResult.getData()) { log.error("[handleClientDisconnected][更新设备状态为离线失败] clientId={}, username={}, code={}, msg={}", @@ -136,19 +131,18 @@ public class IotDeviceWebhookVertxHandler implements Handler { * 解析用户名,格式为 deviceName&productKey * * @param username 用户名 - * @return 解析结果,[0]为deviceName,[1]为productKey,解析失败返回null + * @return 解析结果,[0] 为 deviceName,[1] 为productKey,解析失败返回 null */ private String[] parseUsername(String username) { if (StrUtil.isEmpty(username)) { return null; } - String[] parts = username.split("&"); if (parts.length != 2) { - log.warn("[parseUsername][用户名格式不正确,无法解析产品标识和设备名称] username={}", username); + log.warn("[parseUsername][用户名格式({})不正确,无法解析产品标识和设备名称]", username); return null; } - return parts; } + } \ No newline at end of file diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 492e31db56..17403fef85 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -46,11 +46,11 @@ - - - - - + + cn.iocoder.boot + yudao-module-bpm-biz + ${revision} + From 34453a3f70492f9dfbc8b8d70a458632321e6017 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 08:30:27 +0800 Subject: [PATCH 268/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B0=E6=8D=AE=E6=A1=A5?= =?UTF-8?q?=E6=A2=81=E7=9A=84=E7=BB=B4=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/enums/DictTypeConstants.java | 1 + .../admin/rule/IotDataBridgeController.java | 13 +++++++------ .../rule/vo/databridge/IotDataBridgePageReqVO.java | 2 ++ .../rule/vo/databridge/IotDataBridgeRespVO.java | 4 ++-- .../rule/vo/databridge/IotDataBridgeSaveReqVO.java | 5 +++-- ...onfig.java => IotDataBridgeAbstractConfig.java} | 2 +- .../databridge/config/IotDataBridgeHttpConfig.java | 2 +- .../config/IotDataBridgeKafkaMQConfig.java | 2 +- .../databridge/config/IotDataBridgeMqttConfig.java | 2 +- .../config/IotDataBridgeRabbitMQConfig.java | 2 +- .../config/IotDataBridgeRedisStreamMQConfig.java | 2 +- .../config/IotDataBridgeRocketMQConfig.java | 2 +- .../iot/dal/dataobject/rule/IotDataBridgeDO.java | 4 ++-- .../iot/service/rule/IotDataBridgeService.java | 14 +++++++------- .../AbstractCacheableDataBridgeExecute.java | 8 ++++---- .../action/databridge/IotDataBridgeExecute.java | 1 + .../databridge/IotRocketMQDataBridgeExecute.java | 1 + yudao-server/pom.xml | 1 + 18 files changed, 38 insertions(+), 30 deletions(-) rename yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/{IotDataBridgeConfig.java => IotDataBridgeAbstractConfig.java} (95%) diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java index 1fc47c0c00..d8f0cc60d2 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java @@ -15,6 +15,7 @@ public class DictTypeConstants { public static final String VALIDATE_TYPE = "iot_validate_type"; public static final String DEVICE_STATE = "iot_device_state"; + public static final String IOT_DATA_BRIDGE_DIRECTION_ENUM = "iot_data_bridge_direction_enum"; public static final String IOT_DATA_BRIDGE_TYPE_ENUM = "iot_data_bridge_type_enum"; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java index 2d2f8fb75b..f6ba161234 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java @@ -37,14 +37,14 @@ public class IotDataBridgeController { private IotDataBridgeService dataBridgeService; @PostMapping("/create") - @Operation(summary = "创建IoT 数据桥梁") + @Operation(summary = "创建数据桥梁") @PreAuthorize("@ss.hasPermission('iot:data-bridge:create')") public CommonResult createDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO createReqVO) { return success(dataBridgeService.createDataBridge(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新IoT 数据桥梁") + @Operation(summary = "更新数据桥梁") @PreAuthorize("@ss.hasPermission('iot:data-bridge:update')") public CommonResult updateDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO updateReqVO) { dataBridgeService.updateDataBridge(updateReqVO); @@ -52,7 +52,7 @@ public class IotDataBridgeController { } @DeleteMapping("/delete") - @Operation(summary = "删除IoT 数据桥梁") + @Operation(summary = "删除数据桥梁") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('iot:data-bridge:delete')") public CommonResult deleteDataBridge(@RequestParam("id") Long id) { @@ -61,7 +61,7 @@ public class IotDataBridgeController { } @GetMapping("/get") - @Operation(summary = "获得IoT 数据桥梁") + @Operation(summary = "获得数据桥梁") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')") public CommonResult getDataBridge(@RequestParam("id") Long id) { @@ -70,15 +70,16 @@ public class IotDataBridgeController { } @GetMapping("/page") - @Operation(summary = "获得IoT 数据桥梁分页") + @Operation(summary = "获得数据桥梁分页") @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')") public CommonResult> getDataBridgePage(@Valid IotDataBridgePageReqVO pageReqVO) { PageResult pageResult = dataBridgeService.getDataBridgePage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class)); } + // TODO @puhui999:不用导出哈。相关的 IotDataBridgeRespVO 里的导出也注释掉哈 @GetMapping("/export-excel") - @Operation(summary = "导出IoT 数据桥梁 Excel") + @Operation(summary = "导出数据桥梁 Excel") @PreAuthorize("@ss.hasPermission('iot:data-bridge:export')") @ApiAccessLog(operateType = EXPORT) public void exportDataBridgeExcel(@Valid IotDataBridgePageReqVO pageReqVO, diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java index 7da87be07a..a3f8007c31 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java @@ -20,6 +20,7 @@ public class IotDataBridgePageReqVO extends PageParam { @Schema(description = "桥梁名称", example = "赵六") private String name; + // TODO @puhui999:description、direction、type 不过滤哈 @Schema(description = "桥梁描述", example = "随便") private String description; @@ -36,4 +37,5 @@ public class IotDataBridgePageReqVO extends PageParam { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java index 3e50dc4d5b..a8faf40d60 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; @@ -48,7 +48,7 @@ public class IotDataBridgeRespVO { @Schema(description = "桥梁配置") @ExcelProperty("桥梁配置") - private IotDataBridgeConfig config; + private IotDataBridgeAbstractConfig config; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @ExcelProperty("创建时间") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java index 96620a1ca4..37dc9b218f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -24,6 +24,7 @@ public class IotDataBridgeSaveReqVO { @NotNull(message = "桥梁状态不能为空") private Integer status; + // TODO @puhui999:枚举的校验 @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "桥梁方向不能为空") private Integer direction; @@ -34,6 +35,6 @@ public class IotDataBridgeSaveReqVO { @Schema(description = "桥梁配置") @NotNull(message = "桥梁配置不能为空") - private IotDataBridgeConfig config; + private IotDataBridgeAbstractConfig config; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java similarity index 95% rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java index f37a51edb3..06219f7236 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java @@ -22,7 +22,7 @@ import lombok.Data; @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "REDIS_STREAM"), @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "ROCKETMQ"), }) -public abstract class IotDataBridgeConfig { +public abstract class IotDataBridgeAbstractConfig { /** * 配置类型 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java index 69cdc71f7f..9711cb6ec9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java @@ -10,7 +10,7 @@ import java.util.Map; * @author HUIHUI */ @Data -public class IotDataBridgeHttpConfig extends IotDataBridgeConfig { +public class IotDataBridgeHttpConfig extends IotDataBridgeAbstractConfig { /** * 请求 URL diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java index 3acd646f33..cbc37cd691 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java @@ -8,7 +8,7 @@ import lombok.Data; * @author HUIHUI */ @Data -public class IotDataBridgeKafkaMQConfig extends IotDataBridgeConfig { +public class IotDataBridgeKafkaMQConfig extends IotDataBridgeAbstractConfig { /** * Kafka 服务器地址 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java index 0bf7067bc1..c437898c23 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java @@ -8,7 +8,7 @@ import lombok.Data; * @author HUIHUI */ @Data -public class IotDataBridgeMqttConfig extends IotDataBridgeConfig { +public class IotDataBridgeMqttConfig extends IotDataBridgeAbstractConfig { /** * MQTT 服务器地址 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java index 29bf328979..40e72f14c9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java @@ -8,7 +8,7 @@ import lombok.Data; * @author HUIHUI */ @Data -public class IotDataBridgeRabbitMQConfig extends IotDataBridgeConfig { +public class IotDataBridgeRabbitMQConfig extends IotDataBridgeAbstractConfig { /** * RabbitMQ 服务器地址 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java index db7b2b2bcb..288b772a02 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java @@ -8,7 +8,7 @@ import lombok.Data; * @author HUIHUI */ @Data -public class IotDataBridgeRedisStreamMQConfig extends IotDataBridgeConfig { +public class IotDataBridgeRedisStreamMQConfig extends IotDataBridgeAbstractConfig { /** * Redis 服务器地址 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java index e911461e4d..791d362508 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java @@ -8,7 +8,7 @@ import lombok.Data; * @author HUIHUI */ @Data -public class IotDataBridgeRocketMQConfig extends IotDataBridgeConfig { +public class IotDataBridgeRocketMQConfig extends IotDataBridgeAbstractConfig { /** * RocketMQ 名称服务器地址 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index 05493b916f..488c451aa2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeConfig; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -63,6 +63,6 @@ public class IotDataBridgeDO extends BaseDO { * 桥梁配置 */ @TableField(typeHandler = JacksonTypeHandler.class) - private IotDataBridgeConfig config; + private IotDataBridgeAbstractConfig config; } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java index f720a1d15d..18069376b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotDataBridgeService.java @@ -14,7 +14,7 @@ import jakarta.validation.Valid; public interface IotDataBridgeService { /** - * 创建IoT 数据桥梁 + * 创建数据桥梁 * * @param createReqVO 创建信息 * @return 编号 @@ -22,32 +22,32 @@ public interface IotDataBridgeService { Long createDataBridge(@Valid IotDataBridgeSaveReqVO createReqVO); /** - * 更新IoT 数据桥梁 + * 更新数据桥梁 * * @param updateReqVO 更新信息 */ void updateDataBridge(@Valid IotDataBridgeSaveReqVO updateReqVO); /** - * 删除IoT 数据桥梁 + * 删除数据桥梁 * * @param id 编号 */ void deleteDataBridge(Long id); /** - * 获得IoT 数据桥梁 + * 获得数据桥梁 * * @param id 编号 - * @return IoT 数据桥梁 + * @return 数据桥梁 */ IotDataBridgeDO getDataBridge(Long id); /** - * 获得IoT 数据桥梁分页 + * 获得数据桥梁分页 * * @param pageReqVO 分页查询 - * @return IoT 数据桥梁分页 + * @return 数据桥梁分页 */ PageResult getDataBridgePage(IotDataBridgePageReqVO pageReqVO); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java index d26c2dd436..e7f84dd6ca 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/AbstractCacheableDataBridgeExecute.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; +import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import com.google.common.cache.CacheBuilder; @@ -56,6 +57,7 @@ public abstract class AbstractCacheableDataBridgeExecute imple } }) .build(new CacheLoader() { + @Override public Producer load(Config config) throws Exception { try { @@ -67,6 +69,7 @@ public abstract class AbstractCacheableDataBridgeExecute imple throw e; // 抛出异常,触发缓存加载失败机制 } } + }); /** @@ -98,12 +101,9 @@ public abstract class AbstractCacheableDataBridgeExecute imple @Override @SuppressWarnings({"unchecked"}) public void execute(IotDeviceMessage message, IotDataBridgeDO dataBridge) { - // 1.1 校验数据桥梁类型 - if (!getType().equals(dataBridge.getType())) { + if (ObjUtil.notEqual(message.getType(), getType())) { return; } - - // 1.2 执行对应的数据桥梁发送消息 try { execute0(message, (Config) dataBridge.getConfig()); } catch (Exception e) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index ce3d0f1938..1e8d939ec2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index 541bd181e0..c3e729dda3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -66,6 +66,7 @@ public class IotRocketMQDataBridgeExecute extends } // TODO @芋艿:测试代码,后续清理 + // TODO @puhui999:搞到测试类里。 public static void main(String[] args) { // 1. 创建一个共享的实例 IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 2c7708a517..f408ca3e5d 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -115,6 +115,7 @@ ${revision} + org.apache.rocketmq rocketmq-spring-boot-starter From 25a0fe908a6a9a272fb76b5ef43058b421354c98 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 12:51:50 +0800 Subject: [PATCH 269/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20function=20ca?= =?UTF-8?q?ll=20=E7=A4=BA=E4=BE=8B=E3=80=82=E4=BC=9A=E7=BB=A7=E7=BB=AD?= =?UTF-8?q?=E5=AE=8C=E5=96=84=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/service/tool/ListDirTool.java | 95 +++++++++++++++++ .../module/ai/service/tool/ListDirToolB.java | 100 ++++++++++++++++++ .../ai/config/YudaoAiAutoConfiguration.java | 13 +-- .../ai/core/factory/AiModelFactoryImpl.java | 9 +- .../yudao/framework/ai/core/util/AiUtils.java | 14 ++- .../yudao/framework/ai/package-info.java | 7 +- 6 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java new file mode 100644 index 0000000000..4315e549ae --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.ai.service.tool; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.ai.tool.annotation.ToolParam; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 目录内容列表工具:列出指定目录的内容 + * + * @author 芋道源码 + */ +@Component +public class ListDirTool { + + /** + * 列出指定目录的内容 + * + * @param relativePath 要列出内容的目录路径,相对于工作区根目录 + * @return 目录内容列表 + */ + @Tool(name = "listDir", description = "列出指定目录的内容") + public Response listDir(@ToolParam(description = "要列出内容的目录路径,相对于工作区根目录") String relativePath) { + // 校验目录存在 + String path = StrUtil.blankToDefault(relativePath, "."); + Path dirPath = Paths.get(path); + if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { + return new Response(Collections.emptyList()); + } + // 列出目录内容 + File[] files = dirPath.toFile().listFiles(); + if (ArrayUtil.isEmpty(files)) { + return new Response(Collections.emptyList()); + } + return new Response(convertList(Arrays.asList(files), file -> new Response.File() + .setDirectory(file.isDirectory()).setName(file.getName()) + .setSize(file.isFile() ? FileUtil.readableFileSize(file.length()) : null) + .setLastModified( + LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN)))); + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Response { + + /** + * 目录内容列表 + */ + private List files; + + @Data + public static class File { + + /** + * 是否为目录 + */ + private Boolean directory; + + /** + * 名称 + */ + private String name; + + /** + * 大小,仅对文件有效 + */ + private String size; + + /** + * 最后修改时间 + */ + private String lastModified; + + } + + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java new file mode 100644 index 0000000000..3eafff4b2c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.ai.service.tool; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 目录内容列表工具:列出指定目录的内容 + * + * @author 芋道源码 + */ +@Component("listDir") +public class ListDirToolB implements Function { + + @Data + @JsonClassDescription("列出指定目录的内容") + public static class Request { + + /** + * 要列出内容的目录路径 + */ + @JsonPropertyDescription("要列出内容的目录路径,例如说:/Users/yunai") + private String path; + + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Response { + + /** + * 目录内容列表 + */ + private List files; + + @Data + public static class File { + + /** + * 是否为目录 + */ + private Boolean directory; + + /** + * 名称 + */ + private String name; + + /** + * 大小,仅对文件有效 + */ + private String size; + + /** + * 最后修改时间 + */ + private String lastModified; + + } + + } + + @Override + public Response apply(Request request) { + // 校验目录存在 + String path = StrUtil.blankToDefault(request.getPath(), "."); + Path dirPath = Paths.get(path); + if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { + return new Response(Collections.emptyList()); + } + // 列出目录内容 + File[] files = dirPath.toFile().listFiles(); + if (ArrayUtil.isEmpty(files)) { + return new Response(Collections.emptyList()); + } + return new Response(convertList(Arrays.asList(files), file -> + new Response.File().setDirectory(file.isDirectory()).setName(file.getName()) + .setLastModified(LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN)))); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index bea8e5bc0d..720d933087 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -33,7 +33,7 @@ import org.springframework.context.annotation.Bean; * @author fansili */ @AutoConfiguration -@EnableConfigurationProperties({YudaoAiProperties.class, +@EnableConfigurationProperties({ YudaoAiProperties.class, QdrantVectorStoreProperties.class, // 解析 Qdrant 配置 RedisVectorStoreProperties.class, // 解析 Redis 配置 MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 解析 Milvus 配置 @@ -139,15 +139,16 @@ public class YudaoAiAutoConfiguration { } // 特殊:由于混元大模型不提供 deepseek,而是通过知识引擎,所以需要区分下 URL if (StrUtil.isEmpty(properties.getBaseUrl())) { - properties.setBaseUrl(StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? - HunYuanChatModel.DEEP_SEEK_BASE_URL : HunYuanChatModel.BASE_URL); + properties.setBaseUrl( + StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL + : HunYuanChatModel.BASE_URL); } // 创建 OpenAiChatModel、HunYuanChatModel 对象 OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() - .baseUrl(properties.getBaseUrl()) - .apiKey(properties.getApiKey()) - .build()) + .baseUrl(properties.getBaseUrl()) + .apiKey(properties.getApiKey()) + .build()) .defaultOptions(OpenAiChatOptions.builder() .model(properties.getModel()) .temperature(properties.getTemperature()) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index dbbbbe2241..8565294b0c 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -61,6 +61,7 @@ import org.springframework.ai.minimax.MiniMaxChatModel; import org.springframework.ai.minimax.MiniMaxEmbeddingModel; import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; import org.springframework.ai.minimax.api.MiniMaxApi; +import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.moonshot.MoonshotChatModel; import org.springframework.ai.moonshot.api.MoonshotApi; import org.springframework.ai.ollama.OllamaChatModel; @@ -431,7 +432,7 @@ public class AiModelFactoryImpl implements AiModelFactory { private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); - return OpenAiChatModel.builder().openAiApi(openAiApi).build(); + return OpenAiChatModel.builder().openAiApi(openAiApi).toolCallingManager(getToolCallingManager()).build(); } // TODO @芋艿:手头暂时没密钥,使用建议再测试下 @@ -465,7 +466,7 @@ public class AiModelFactoryImpl implements AiModelFactory { */ private static OllamaChatModel buildOllamaChatModel(String url) { OllamaApi ollamaApi = new OllamaApi(url); - return OllamaChatModel.builder().ollamaApi(ollamaApi).build(); + return OllamaChatModel.builder().ollamaApi(ollamaApi).toolCallingManager(getToolCallingManager()).build(); } private StabilityAiImageModel buildStabilityAiImageModel(String apiKey, String url) { @@ -699,4 +700,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(BatchingStrategy.class); } + private static ToolCallingManager getToolCallingManager() { + return SpringUtil.getBean(ToolCallingManager.class); + } + } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index c3a87358cd..b15fb2e54d 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -24,14 +24,18 @@ public class AiUtils { // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: + // TODO functions return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens).build(); case YI_YAN: return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: + // TODO functions return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case MINI_MAX: + // TODO functions return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case MOONSHOT: + // TODO functions return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case OPENAI: case DEEP_SEEK: // 复用 OpenAI 客户端 @@ -39,12 +43,18 @@ public class AiUtils { case HUN_YUAN: // 复用 OpenAI 客户端 case XING_HUO: // 复用 OpenAI 客户端 case SILICON_FLOW: // 复用 OpenAI 客户端 - return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); + return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) +// .toolNames() TODO + .toolNames("listDir") + .build(); case AZURE_OPENAI: // TODO 芋艿:貌似没 model 字段???! + // TODO 芋艿:.toolNames() TODO return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens).build(); case OLLAMA: - return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens).build(); + // TODO 芋艿:.toolNames() TODO + return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) + .toolNames("listDir").build(); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/package-info.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/package-info.java index b02d1b3cef..e4655cd0b7 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/package-info.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/package-info.java @@ -4,7 +4,10 @@ * models 包路径: * 1. xinghuo 包:【讯飞】星火,自己实现 * 2. deepseek 包:【深度求索】DeepSeek,自己实现 - * 3. midjourney 包:Midjourney API,对接 https://github.com/novicezk/midjourney-proxy 实现 - * 4. suno 包:Suno API,对接 https://github.com/gcui-art/suno-api 实现 + * 3. doubao 包:【字节豆包】DouBao,自己实现 + * 4. hunyuan 包:【腾讯混元】HunYuan,自己实现 + * 5. siliconflow 包:【硅基硅流】SiliconFlow,自己实现 + * 6. midjourney 包:Midjourney API,对接 https://github.com/novicezk/midjourney-proxy 实现 + * 7. suno 包:Suno API,对接 https://github.com/gcui-art/suno-api 实现 */ package cn.iocoder.yudao.framework.ai; \ No newline at end of file From b87a5838424e4e091e2df22c74b69722d3adfd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=8E=84=E7=A4=BC?= Date: Thu, 13 Mar 2025 22:16:45 +0800 Subject: [PATCH 270/309] =?UTF-8?q?refactor(iot):=20=E9=87=8D=E6=9E=84=20O?= =?UTF-8?q?TA=20=E5=8D=87=E7=BA=A7=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新错误码定义,调整 OTA 相关错误码的编号和描述 - 移除未使用的 IotOtaFirmwareCommonReqVO 类- 优化 IotOtaFirmwareCreateReqVO 和 IotOtaFirmwareUpdateReqVO 的结构 - 删除冗余的 IotOtaUpgradeRecordCreateReqBO 类 - 重构 IotOtaUpgradeRecordMapper 的查询方法 - 更新 IotOtaUpgradeRecordService 接口,简化升级记录创建方法 - 删除未使用的 IotOtaUpgradeRecordJob 类 - 优化 IotOtaUpgradeRecordController 的重试接口,使用 PUT 方法 --- .../module/iot/enums/ErrorCodeConstants.java | 17 ++- .../ota/IotOtaUpgradeRecordController.java | 3 +- .../ota/IotOtaUpgradeTaskController.java | 3 +- .../firmware/IotOtaFirmwareCommonReqVO.java | 27 ---- .../firmware/IotOtaFirmwareCreateReqVO.java | 64 ++------- .../firmware/IotOtaFirmwareUpdateReqVO.java | 17 ++- .../record/IotOtaUpgradeRecordPageReqVO.java | 26 ---- .../task/IotOtaUpgradeTaskSaveReqVO.java | 9 -- .../ota/IotOtaUpgradeRecordConvert.java | 24 ---- .../dataobject/ota/IotOtaUpgradeTaskDO.java | 9 -- .../mysql/ota/IotOtaUpgradeRecordMapper.java | 56 ++++---- .../iot/job/ota/IotOtaUpgradeRecordJob.java | 35 ----- .../iot/job/ota/IotOtaUpgradeTaskJob.java | 72 ---------- .../service/ota/IotOtaFirmwareService.java | 6 +- .../ota/IotOtaFirmwareServiceImpl.java | 23 ++- .../ota/IotOtaUpgradeRecordService.java | 24 +--- .../ota/IotOtaUpgradeRecordServiceImpl.java | 121 ++++++++-------- .../service/ota/IotOtaUpgradeTaskService.java | 6 +- .../ota/IotOtaUpgradeTaskServiceImpl.java | 134 ++++++------------ .../bo/IotOtaUpgradeRecordCreateReqBO.java | 79 ----------- .../bo/IotOtaUpgradeRecordUpdateReqBO.java | 46 ------ 21 files changed, 201 insertions(+), 600 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java index 3d12c4b594..f9081e1fae 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java @@ -56,16 +56,15 @@ public interface ErrorCodeConstants { ErrorCode OTA_FIRMWARE_NOT_EXISTS = new ErrorCode(1_050_008_000, "固件信息不存在"); ErrorCode OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE = new ErrorCode(1_050_008_001, "产品版本号重复"); - // TODO @li:1_050_008_100,这样有点间隔? - ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_002, "升级任务不存在"); - ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_003, "升级任务名称重复"); - ErrorCode OTA_UPGRADE_TASK_PARAMS_INVALID = new ErrorCode(1_050_008_004, "升级任务参数无效"); - ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_005, "升级任务不能取消"); + ErrorCode OTA_UPGRADE_TASK_NOT_EXISTS = new ErrorCode(1_050_008_100, "升级任务不存在"); + ErrorCode OTA_UPGRADE_TASK_NAME_DUPLICATE = new ErrorCode(1_050_008_101, "升级任务名称重复"); + ErrorCode OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY = new ErrorCode(1_050_008_102, "设备编号列表不能为空"); + ErrorCode OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY = new ErrorCode(1_050_008_103, "设备列表不能为空"); + ErrorCode OTA_UPGRADE_TASK_CANNOT_CANCEL = new ErrorCode(1_050_008_104, "升级任务不能取消"); - // TODO @li:1_050_008_200 - ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_006, "升级记录不存在"); - ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_007, "升级记录重复"); - ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_008, "升级记录不能重试"); + ErrorCode OTA_UPGRADE_RECORD_NOT_EXISTS = new ErrorCode(1_050_008_200, "升级记录不存在"); + ErrorCode OTA_UPGRADE_RECORD_DUPLICATE = new ErrorCode(1_050_008_201, "升级记录重复"); + ErrorCode OTA_UPGRADE_RECORD_CANNOT_RETRY = new ErrorCode(1_050_008_202, "升级记录不能重试"); // ========== MQTT 通信相关 1-050-009-000 ========== ErrorCode MQTT_TOPIC_ILLEGAL = new ErrorCode(1_050_009_000, "topic illegal"); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java index dcff396480..519d6b9ab8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java @@ -64,8 +64,7 @@ public class IotOtaUpgradeRecordController { return success(BeanUtils.toBean(upgradeRecord, IotOtaUpgradeRecordRespVO.class)); } - // TODO @li:使用 Putmapping - @PostMapping("/retry") + @PutMapping("/retry") @Operation(summary = "重试升级记录") @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:retry')") @Parameter(name = "id", description = "升级记录编号", required = true, example = "1024") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java index 4f560d5b5c..5486102e6d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java @@ -44,11 +44,10 @@ public class IotOtaUpgradeTaskController { return success(true); } - // TODO @li:get 接口,不是 @RequestBody 哈 @GetMapping("/page") @Operation(summary = "获得升级任务分页") @PreAuthorize(value = "@ss.hasPermission('iot:ota-upgrade-task:query')") - public CommonResult> getUpgradeTaskPage(@Valid @RequestBody IotOtaUpgradeTaskPageReqVO pageReqVO) { + public CommonResult> getUpgradeTaskPage(@Valid IotOtaUpgradeTaskPageReqVO pageReqVO) { PageResult pageResult = upgradeTaskService.getUpgradeTaskPage(pageReqVO); return success(BeanUtils.toBean(pageResult, IotOtaUpgradeTaskRespVO.class)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java deleted file mode 100644 index 2799907cea..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCommonReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; - -// TODO @li:因为 create 和 update 可以公用的字段比较少,建议不用 IotOtaFirmwareCommonReqVO -@Data -@Schema(description = "管理后台 - OTA固件信息 Request VO") -public class IotOtaFirmwareCommonReqVO { - - /** - * 固件名称 - */ - @NotEmpty(message = "固件名称不能为空") - @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件") - private String name; - - /** - * 固件描述 - */ - @Schema(description = "固件描述", example = "某品牌型号固件,测试用") - private String description; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java index b194cde776..98b99351de 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java @@ -7,72 +7,32 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -// TODO @li:中英文之间,有个空格。中文写作习惯哈。 -@Schema(description = "管理后台 - OTA固件创建 Request VO") +@Schema(description = "管理后台 - OTA 固件创建 Request VO") @Data -public class IotOtaFirmwareCreateReqVO extends IotOtaFirmwareCommonReqVO { +public class IotOtaFirmwareCreateReqVO { + + @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件") + @NotEmpty(message = "固件名称不能为空") + private String name; + + @Schema(description = "固件描述", example = "某品牌型号固件,测试用") + private String description; - // TODO @li:因为有了注解,注释可以不写哈 - // TODO @li:swagger 注解,写在 validator 注解之前,保持项目统一哈。 - /** - * 版本号 - */ - @NotEmpty(message = "版本号不能为空") @Schema(description = "版本号", requiredMode = REQUIRED, example = "1.0.0") + @NotEmpty(message = "版本号不能为空") private String version; - /** - * 产品编号 - *

- * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} - */ - @NotNull(message = "产品编号不能为空") @Schema(description = "产品编号", requiredMode = REQUIRED, example = "1024") + @NotNull(message = "产品编号不能为空") private String productId; - // TODO @li:productId 即可,而 productKey 通过 productId 查询 - /** - * 产品标识 - *

- * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()} - */ - @NotEmpty(message = "产品标识不能为空") - @Schema(description = "产品标识", requiredMode = REQUIRED, example = "yudao") - private String productKey; - - /** - * 签名方式 - *

- * 例如说:MD5、SHA256 - */ @Schema(description = "签名方式", example = "MD5") private String signMethod; - // TODO @li:fileSign、fileSize 通过后端下载文件,计算出来。对前端屏蔽这个细节。 - - /** - * 固件文件签名 - */ - @Schema(description = "固件文件签名", example = "d41d8cd98f00b204e9800998ecf8427e") - private String fileSign; - - /** - * 固件文件大小 - */ - @NotNull(message = "固件文件大小不能为空") - @Schema(description = "固件文件大小(单位:byte)", example = "1024") - private Long fileSize; - - /** - * 固件文件 URL - */ - @NotEmpty(message = "固件文件 URL 不能为空") @Schema(description = "固件文件 URL", requiredMode = REQUIRED, example = "https://www.iocoder.cn/yudao-firmware.zip") + @NotEmpty(message = "固件文件 URL 不能为空") private String fileUrl; - /** - * 自定义信息,建议使用 JSON 格式 - */ @Schema(description = "自定义信息,建议使用 JSON 格式", example = "{\"key1\":\"value1\",\"key2\":\"value2\"}") private String information; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java index eacbfd2dd7..88b7cde5cb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java @@ -1,20 +1,25 @@ package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -@Data @Schema(description = "管理后台 - OTA固件更新 Request VO") -public class IotOtaFirmwareUpdateReqVO extends IotOtaFirmwareCommonReqVO { +@Data +public class IotOtaFirmwareUpdateReqVO { - /** - * 固件编号 - */ - @NotNull(message = "固件编号不能为空") @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024") + @NotNull(message = "固件编号不能为空") private Long id; + @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件") + @NotEmpty(message = "固件名称不能为空") + private String name; + + @Schema(description = "固件描述", example = "某品牌型号固件,测试用") + private String description; + } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java index 6e083a3323..e28024085f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java @@ -11,32 +11,6 @@ import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Schema(description = "管理后台 - OTA升级记录分页 Request VO") public class IotOtaUpgradeRecordPageReqVO extends PageParam { - // TODO @li:使用 IotOtaUpgradeRecordStatusEnum 枚举哈 - /** - * 待处理状态 - */ - public static final Integer PENDING = 0; - /** - * 已推送状态 - */ - public static final Integer PUSHED = 10; - /** - * 正在升级状态 - */ - public static final Integer UPGRADING = 20; - /** - * 升级成功状态 - */ - public static final Integer SUCCESS = 30; - /** - * 升级失败状态 - */ - public static final Integer FAILURE = 40; - /** - * 升级已取消状态 - */ - public static final Integer CANCELED = 50; - /** * 升级任务编号字段。 *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java index 7cb25299fb..e2fbd8efe6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java @@ -57,13 +57,4 @@ public class IotOtaUpgradeTaskSaveReqVO { @Schema(description = "选中的设备编号数组", requiredMode = REQUIRED, example = "[1,2,3,4]") private List deviceIds; - // TODO @li:通过 deviceIds 查询 deviceNames,前端不传递哈 - /** - * 选中的设备名字数组 - *

- * 关联 {@link IotDeviceDO#getDeviceName()} - */ - @Schema(description = "选中的设备名字数组", requiredMode = REQUIRED, example = "[设备1,设备2,设备3,设备4]") - private List deviceNames; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java index 2da9315f7d..fe5279b3c6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java @@ -1,35 +1,11 @@ package cn.iocoder.yudao.module.iot.convert.ota; -import cn.hutool.core.convert.Convert; -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; -import java.util.List; - @Mapper public interface IotOtaUpgradeRecordConvert { IotOtaUpgradeRecordConvert INSTANCE = Mappers.getMapper(IotOtaUpgradeRecordConvert.class); - // TODO @li:一般情况下,这种 convert 直接写 service 就好啦。不用特别写一个哈 - default List convertBOList(IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceList) { - return deviceList.stream().map(device -> { - IotOtaUpgradeRecordCreateReqBO createReqBO = new IotOtaUpgradeRecordCreateReqBO(); - createReqBO.setFirmwareId(firmware.getId()); - createReqBO.setTaskId(upgradeTask.getId()); - createReqBO.setProductKey(device.getProductKey()); - createReqBO.setDeviceName(device.getDeviceName()); - createReqBO.setDeviceId(Convert.toStr(device.getId())); - createReqBO.setFromFirmwareId(Convert.toLong(device.getFirmwareId())); - createReqBO.setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); - createReqBO.setProgress(0); - return createReqBO; - }).toList(); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java index e98e6c15c2..6cc80e2b76 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java @@ -71,13 +71,4 @@ public class IotOtaUpgradeTaskDO extends BaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private List deviceIds; - // TODO @li:这个通过查询,不用冗余 - /** - * 选中的设备名字数组 - *

- * 关联 {@link IotDeviceDO#getDeviceName()} - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List deviceNames; - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index b48131c61a..46ac870849 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -8,8 +8,10 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; import java.util.List; +import java.util.Map; /** * OTA 升级记录 Mapper @@ -36,36 +38,42 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX() - .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, taskId) - .likeIfPresent(IotOtaUpgradeRecordDO::getDeviceId, deviceName) - .eqIfPresent(IotOtaUpgradeRecordDO::getStatus, status)); - } + @Select("select count(case when status = 0 then 1 else 0) as `0` " + + "count(case when status = 1 then 1 else 0) as `1` " + + "count(case when status = 2 then 1 else 0) as `2` " + + "count(case when status = 3 then 1 else 0) as `3` " + + "count(case when status = 4 then 1 else 0) as `4` " + + "count(case when status = 5 then 1 else 0) as `5` " + + "from iot_ota_upgrade_record " + + "where task_id = #{taskId} " + + "and device_name like concat('%', #{deviceName}, '%') " + + "and status = #{status}") + List> selectOtaUpgradeRecordCount(@Param("taskId") Long taskId, + @Param("deviceName") String deviceName); /** - * 获取OTA升级记录的统计信息 + * 根据固件ID查询OTA升级记录的状态统计信息。 + * 该函数通过SQL查询统计不同状态(0到5)的记录数量,并返回一个包含统计结果的Map列表。 * - * @param firmwareId 固件ID,用于筛选特定固件的升级记录 - * @param status 状态,用于筛选特定状态的升级记录 - * @return 返回符合条件的OTA升级记录的统计信息 + * @param firmwareId 固件ID,用于筛选特定固件的OTA升级记录。 + * @return 返回一个Map列表,每个Map包含不同状态(0到5)的记录数量。 */ - default Long getOtaUpgradeRecordStatistics(@Param("firmwareId") Long firmwareId, - @Param("status") Integer status) { - return selectCount(new LambdaQueryWrapperX() - .eqIfPresent(IotOtaUpgradeRecordDO::getFirmwareId, firmwareId) - .eqIfPresent(IotOtaUpgradeRecordDO::getStatus, status)); - } - + @Select("select count(case when status = 0 then 1 else 0) as `0` " + + "count(case when status = 1 then 1 else 0) as `1` " + + "count(case when status = 2 then 1 else 0) as `2` " + + "count(case when status = 3 then 1 else 0) as `3` " + + "count(case when status = 4 then 1 else 0) as `4` " + + "count(case when status = 5 then 1 else 0) as `5` " + + "from iot_ota_upgrade_record " + + "where firmware_id = #{firmwareId}") + List> selectOtaUpgradeRecordStatistics(Long firmwareId); /** * 根据分页查询条件获取IOT OTA升级记录的分页结果 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java deleted file mode 100644 index 86c44db8b5..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeRecordJob.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.iot.job.ota; - -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeRecordService; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.List; - -@Component -public class IotOtaUpgradeRecordJob implements JobHandler { - - @Resource - private IotOtaUpgradeRecordService upgradeRecordService; - - @Override - @TenantJob - public String execute(String param) throws Exception { - // 1. 查询待处理的升级记录 - List upgradeRecords = upgradeRecordService - .getUpgradeRecordListByState(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); - - // TODO @芋艿 2.执行升级动作 - // TODO @li:应该是逐条 push,逐条更新。不用批量哈 - - // 3. 最终,更新升级记录状态 - List ids = upgradeRecords.stream().map(IotOtaUpgradeRecordDO::getId).toList(); - upgradeRecordService.updateUpgradeRecordStatus(ids, IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); - return ""; - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java deleted file mode 100644 index 5465161f98..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/ota/IotOtaUpgradeTaskJob.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.iot.job.ota; - -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeRecordService; -import cn.iocoder.yudao.module.iot.service.ota.IotOtaUpgradeTaskService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.List; - -// TODO @li:也不用通过 job 去统计。可以通过 record update status 后,主动去更新 task 的状态。 -@Slf4j -@Component -public class IotOtaUpgradeTaskJob implements JobHandler { - - @Resource - private IotOtaUpgradeTaskService upgradeTaskService; - @Resource - private IotOtaUpgradeRecordService upgradeRecordService; - - @Override - @TenantJob - public String execute(String param) throws Exception { - // 1.这个任务主要是为了检查并更新升级任务的状态 - List upgradeTasks = upgradeTaskService - .getUpgradeTaskByState(IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus()); - // 2.遍历并且确定升级任务的状态 - upgradeTasks.forEach(this::checkUpgradeTaskState); - // TODO @芋艿: 其他的一些业务逻辑 - return ""; - } - - private void checkUpgradeTaskState(IotOtaUpgradeTaskDO upgradeTask) { - // 1.查询任务所有的升级记录 - List upgradeRecords = - upgradeRecordService.getUpgradeRecordListByTaskId(upgradeTask.getId()); - if (upgradeRecords.stream().anyMatch(upgradeRecord -> - ObjectUtils.equalsAny(upgradeRecord.getStatus(), - IotOtaUpgradeRecordStatusEnum.PENDING.getStatus(), - IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus(), - IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()))) { - // 如果存在正在升级的升级记录,则升级任务的状态为进行中 - log.debug("升级任务 {} 状态为进行中", upgradeTask.getId()); - } else if (upgradeRecords.stream().allMatch(upgradeRecord -> - ObjectUtils.equalsAny(upgradeRecord.getStatus(), - IotOtaUpgradeRecordStatusEnum.SUCCESS.getStatus()))) { - // 如果全部升级成功,则升级任务的状态为已完成 - upgradeTaskService.updateUpgradeTaskStatus(upgradeTask.getId(), - IotOtaUpgradeTaskStatusEnum.COMPLETED.getStatus()); - } else if (upgradeRecords.stream().noneMatch(upgradeRecord -> - ObjectUtils.equalsAny(upgradeRecord.getStatus(), - IotOtaUpgradeRecordStatusEnum.PENDING.getStatus(), - IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus(), - IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus())) && - upgradeRecords.stream().anyMatch(upgradeRecord -> - ObjectUtils.equalsAny(upgradeRecord.getStatus(), - IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus(), - IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()))) { - // 如果全部升级完毕,但是存在升级失败或者取消的升级记录,则升级任务的状态为失败 - upgradeTaskService.updateUpgradeTaskStatus(upgradeTask.getId(), - IotOtaUpgradeTaskStatusEnum.INCOMPLETE.getStatus()); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java index 67e14ce6a5..124c803b39 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java @@ -7,10 +7,10 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import jakarta.validation.Valid; -// TODO @li:类、方法注释有点冗余,可以参考别的模块哈 /** - * OTA固件管理服务接口 - * 提供OTA固件的创建、更新和查询等功能 + * OTA 固件管理 Service + * + * @author Shelly Chan */ public interface IotOtaFirmwareService { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java index 56a830b915..d66ce0e4f6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java @@ -1,14 +1,19 @@ package cn.iocoder.yudao.module.iot.service.ota; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareCreateReqVO; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwarePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwareUpdateReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaFirmwareMapper; +import cn.iocoder.yudao.module.iot.service.product.IotProductService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -26,23 +31,28 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { @Resource private IotOtaFirmwareMapper otaFirmwareMapper; + @Lazy + @Resource + private IotProductService productService; @Override public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) { // 1. 校验固件产品 + 版本号不能重复 - // TODO @li:需要考虑设备也存在 validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion()); - - // 2.转化数据格式,准备存储到数据库中 + // 2.1.转化数据格式,准备存储到数据库中 IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); + // 2.2.查询ProductKey + IotProductDO product = productService.getProduct(Convert.toLong(firmware.getProductId())); + firmware.setProductKey(Objects.requireNonNull(product).getProductKey()); + // TODO @芋艿: 附件、附件签名等属性的计算 + otaFirmwareMapper.insert(firmware); return firmware.getId(); } @Override public void updateOtaFirmware(IotOtaFirmwareUpdateReqVO updateReqVO) { - // TODO @li:如果序号只有一个,直接写 1. 更好哈 - // 1.1. 校验存在 + // 1. 校验存在 validateFirmwareExists(updateReqVO.getId()); // 2. 更新数据 @@ -84,8 +94,7 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { // 查询数据库中是否存在具有相同产品ID和版本号的固件信息 List list = otaFirmwareMapper.selectByProductIdAndVersion(productId, version); // 如果查询结果非空且不为null,则抛出异常,提示固件信息已存在 - // TODO @li:使用 isNotEmpty 这种 方法,简化 - if (Objects.nonNull(list) && !list.isEmpty()) { + if (CollUtil.isNotEmpty(list)) { throw exception(OTA_FIRMWARE_PRODUCT_VERSION_DUPLICATE); } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java index ea5de4a427..86bc18b845 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.service.ota; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; -import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; -import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordUpdateReqBO; import jakarta.validation.Valid; import java.util.List; @@ -16,25 +14,15 @@ import java.util.Map; */ public interface IotOtaUpgradeRecordService { - // TODO @createOtaUpgradeRecordBatch 哈,需要补充方法里,缺少 Ota 关键字的 - /** - * 批量创建物联网OTA升级记录 - *

- * 该函数用于处理一组物联网OTA升级记录的创建请求,并将这些记录批量保存到系统中。 + * 批量创建OTA升级记录。 + * 该函数用于为指定的设备列表、固件ID和升级任务ID创建OTA升级记录。 * - * @param createList 包含多个物联网OTA升级记录创建请求的列表,每个请求对象都经过校验(@Valid注解确保) - * 列表中的每个元素都是IotOtaUpgradeRecordCreateReqBO类型的对象,表示一个独立的升级记录创建请求。 + * @param deviceIds 设备ID列表,表示需要升级的设备集合。 + * @param firmwareId 固件ID,表示要升级到的固件版本。 + * @param upgradeTaskId 升级任务ID,表示此次升级任务的唯一标识。 */ - void createUpgradeRecordBatch(@Valid List createList); - - // TODO @li:尽量避免写比较大的通用 update。而是根据场景提供,这样才能收敛 - /** - * 更新现有的 OTA 升级记录 - * - * @param updateReqBO 包含更新升级记录所需信息的请求对象,必须经过验证。 - */ - void updateUpgradeRecord(@Valid IotOtaUpgradeRecordUpdateReqBO updateReqBO); + void createOtaUpgradeRecordBatch(List deviceIds, Long firmwareId, Long upgradeTaskId); /** * 获取OTA升级记录的数量统计。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java index 5738f57637..c04750e004 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java @@ -1,25 +1,31 @@ package cn.iocoder.yudao.module.iot.service.ota; +import cn.hutool.core.convert.Convert; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record.IotOtaUpgradeRecordPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeRecordDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeRecordMapper; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; -import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordUpdateReqBO; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum.CANCELED; @Slf4j @Service @@ -28,27 +34,40 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Resource private IotOtaUpgradeRecordMapper upgradeRecordMapper; + @Lazy + @Resource + private IotDeviceService deviceService; + @Lazy + @Resource + private IotOtaFirmwareService firmwareService; + @Lazy + @Resource + private IotOtaUpgradeTaskService upgradeTaskService; @Override - public void createUpgradeRecordBatch(List createList) { - // 1. 批量校验参数信息 - createList.forEach(saveBO -> validateUpgradeRecordDuplicate(saveBO.getFirmwareId(), saveBO.getTaskId(), saveBO.getDeviceId())); + public void createOtaUpgradeRecordBatch(List deviceIds, Long firmwareId, Long upgradeTaskId) { + // 1.校验升级记录信息是否存在,并且已经取消的任务可以重新开始 + deviceIds.forEach(deviceId -> validateUpgradeRecordDuplicate(firmwareId, upgradeTaskId, String.valueOf(deviceId))); + // 2.初始化OTA升级记录列表信息 + IotOtaUpgradeTaskDO upgradeTask = upgradeTaskService.getUpgradeTask(upgradeTaskId); + IotOtaFirmwareDO firmware = firmwareService.getOtaFirmware(firmwareId); + List deviceList = deviceService.getDeviceListByIdList(deviceIds); + List upgradeRecordList = deviceList.stream().map(device -> { + IotOtaUpgradeRecordDO upgradeRecord = new IotOtaUpgradeRecordDO(); + upgradeRecord.setFirmwareId(firmware.getId()); + upgradeRecord.setTaskId(upgradeTask.getId()); + upgradeRecord.setProductKey(device.getProductKey()); + upgradeRecord.setDeviceName(device.getDeviceName()); + upgradeRecord.setDeviceId(Convert.toStr(device.getId())); + upgradeRecord.setFromFirmwareId(Convert.toLong(device.getFirmwareId())); + upgradeRecord.setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); + upgradeRecord.setProgress(0); + return upgradeRecord; + }).toList(); + // 3.保存数据 + upgradeRecordMapper.insertBatch(upgradeRecordList); + // TODO @芋艿:在这里需要处理推送升级任务的逻辑 - // 2. 将数据批量存储到数据库里 - List upgradeRecords = BeanUtils.toBean(createList, IotOtaUpgradeRecordDO.class); - upgradeRecordMapper.insertBatch(upgradeRecords); - } - - @Override - @Transactional - public void updateUpgradeRecord(IotOtaUpgradeRecordUpdateReqBO updateReqBO) { - // 1. 校验升级记录信息是否存在 - validateUpgradeRecordExists(updateReqBO.getId()); - - // 2. 将数据转化成数据库存储的格式 - IotOtaUpgradeRecordDO updateRecord = BeanUtils.toBean(updateReqBO, IotOtaUpgradeRecordDO.class); - upgradeRecordMapper.updateById(updateRecord); - // TODO @芋艿: 更新升级记录触发的其他Action } /** @@ -62,21 +81,14 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Transactional public Map getOtaUpgradeRecordCount(IotOtaUpgradeRecordPageReqVO pageReqVO) { // 分别查询不同状态的OTA升级记录数量 - // TODO @li: 通过 groupby 统计下; - Long pending = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); - Long pushed = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); - Long upgrading = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()); - Long success = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.SUCCESS.getStatus()); - Long failure = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus()); - Long canceled = upgradeRecordMapper.getOtaUpgradeRecordCount(pageReqVO.getTaskId(), pageReqVO.getDeviceName(), IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()); - // 将各状态的数量封装到Map中返回 - // TODO @li:使用 MapUtil,因为 Map.of 是 jdk9 才有,后续不好同步到 master 的 jdk8; - return Map.of(IotOtaUpgradeRecordPageReqVO.PENDING, pending, - IotOtaUpgradeRecordPageReqVO.PUSHED, pushed, - IotOtaUpgradeRecordPageReqVO.UPGRADING, upgrading, - IotOtaUpgradeRecordPageReqVO.SUCCESS, success, - IotOtaUpgradeRecordPageReqVO.FAILURE, failure, - IotOtaUpgradeRecordPageReqVO.CANCELED, canceled); + List> upgradeRecordCountList = upgradeRecordMapper.selectOtaUpgradeRecordCount( + pageReqVO.getTaskId(), pageReqVO.getDeviceName()); + Map upgradeRecordCountMap = ObjectUtils.defaultIfNull(upgradeRecordCountList.get(0)); + Objects.requireNonNull(upgradeRecordCountMap); + return upgradeRecordCountMap.entrySet().stream().collect(Collectors.toMap( + entry -> Convert.toInt(entry.getKey()), + entry -> Convert.toLong(entry.getValue()) + )); } /** @@ -90,20 +102,13 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Transactional public Map getOtaUpgradeRecordStatistics(Long firmwareId) { // 查询并统计不同状态的OTA升级记录数量 - // TODO @li: 通过 groupby 统计下; - Long pending = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); - Long pushed = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.PUSHED.getStatus()); - Long upgrading = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.UPGRADING.getStatus()); - Long success = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.SUCCESS.getStatus()); - Long failure = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.FAILURE.getStatus()); - Long canceled = upgradeRecordMapper.getOtaUpgradeRecordStatistics(firmwareId, IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus()); - // 将统计结果封装为Map并返回 - return Map.of(IotOtaUpgradeRecordPageReqVO.PENDING, pending, - IotOtaUpgradeRecordPageReqVO.PUSHED, pushed, - IotOtaUpgradeRecordPageReqVO.UPGRADING, upgrading, - IotOtaUpgradeRecordPageReqVO.SUCCESS, success, - IotOtaUpgradeRecordPageReqVO.FAILURE, failure, - IotOtaUpgradeRecordPageReqVO.CANCELED, canceled); + List> upgradeRecordStatisticsList = upgradeRecordMapper.selectOtaUpgradeRecordStatistics(firmwareId); + Map upgradeRecordStatisticsMap = ObjectUtils.defaultIfNull(upgradeRecordStatisticsList.get(0)); + Objects.requireNonNull(upgradeRecordStatisticsMap); + return upgradeRecordStatisticsMap.entrySet().stream().collect(Collectors.toMap( + entry -> Convert.toInt(entry.getKey()), + entry -> Convert.toLong(entry.getValue()) + )); } @Override @@ -117,8 +122,6 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic upgradeRecordMapper.updateById(new IotOtaUpgradeRecordDO() .setId(upgradeRecord.getId()).setProgress(0) .setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus())); - // TODO @芋艿: 重试升级记录触发的其他Action - // TODO 如果一个升级记录被取消或者已经执行失败,重试成功,是否会对升级任务的状态有影响? } @Override @@ -135,7 +138,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic public void cancelUpgradeRecordByTaskId(Long taskId) { // 暂定只有待推送的升级记录可以取消 upgradeRecordMapper.updateUpgradeRecordStatusByTaskIdAndStatus( - IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus(), taskId, + CANCELED.getStatus(), taskId, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); } @@ -173,21 +176,23 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic } /** - * 验证固件升级记录是否存在。 + * 校验固件升级记录是否重复。 *

- * 该函数通过给定的固件ID、任务ID和设备ID查询升级记录,如果查询结果为空,则抛出异常。 + * 该函数用于检查给定的固件ID、任务ID和设备ID是否已经存在未取消的升级记录。 + * 如果存在未取消的记录,则抛出异常,提示升级记录重复。 * * @param firmwareId 固件ID,用于标识特定的固件版本 * @param taskId 任务ID,用于标识特定的升级任务 * @param deviceId 设备ID,用于标识特定的设备 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出OTA_UPGRADE_RECORD_NOT_EXISTS异常 */ private void validateUpgradeRecordDuplicate(Long firmwareId, Long taskId, String deviceId) { // 根据条件查询升级记录 IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordMapper.selectByConditions(firmwareId, taskId, deviceId); - // 如果查询结果为空,抛出异常 + // 如果查询到升级记录且状态不是已取消,则抛出异常 if (upgradeRecord != null) { - throw exception(OTA_UPGRADE_RECORD_DUPLICATE); + if (!CANCELED.getStatus().equals(upgradeRecord.getStatus())) { + throw exception(OTA_UPGRADE_RECORD_DUPLICATE); + } } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java index 1f5476df15..a2a810bf0c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskService.java @@ -8,10 +8,10 @@ import jakarta.validation.Valid; import java.util.List; -// TODO @li:类、方法注释有点冗余,可以参考别的模块哈 /** - * IoT OTA升级任务服务接口 - * 提供OTA升级任务的创建、取消和查询功能 + * IoT OTA升级任务 Service 接口 + * + * @author Shelly Chan */ public interface IotOtaUpgradeTaskService { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java index cef1448e9a..d20f27e8cf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java @@ -3,9 +3,9 @@ package cn.iocoder.yudao.module.iot.service.ota; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task.IotOtaUpgradeTaskSaveReqVO; -import cn.iocoder.yudao.module.iot.convert.ota.IotOtaUpgradeRecordConvert; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; @@ -13,7 +13,6 @@ import cn.iocoder.yudao.module.iot.dal.mysql.ota.IotOtaUpgradeTaskMapper; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum; import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; -import cn.iocoder.yudao.module.iot.service.ota.bo.IotOtaUpgradeRecordCreateReqBO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; @@ -23,6 +22,7 @@ import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @@ -52,21 +52,13 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { validateFirmwareTaskDuplicate(createReqVO.getFirmwareId(), createReqVO.getName()); // 1.2 校验固件信息是否存在 IotOtaFirmwareDO firmware = firmwareService.validateFirmwareExists(createReqVO.getFirmwareId()); - // 1.3 校验升级范围=2(指定设备时),deviceIds deviceNames不为空并且长度相等 - // TODO @li:deviceNames 应该后端查询 - validateScopeAndDevice(createReqVO.getScope(), createReqVO.getDeviceIds(), createReqVO.getDeviceNames()); - // TODO @li:如果全部范围,但是没设备可以升级,需要报错 - + // 1.3 补全设备范围信息,并且校验是否又设备可以升级,如果没有设备可以升级,则报错 + validateScopeAndDevice(createReqVO.getScope(), createReqVO.getDeviceIds(), firmware.getProductId()); // 2. 保存 OTA 升级任务信息到数据库 - IotOtaUpgradeTaskDO upgradeTask = initUpgradeTask(createReqVO, firmware.getProductId()); + IotOtaUpgradeTaskDO upgradeTask = initOtaUpgradeTask(createReqVO, firmware.getProductId()); upgradeTaskMapper.insert(upgradeTask); - // 3. 生成设备升级记录信息并存储,等待定时任务轮询 - List upgradeRecordList = initUpgradeRecordList( - upgradeTask, firmware, createReqVO.getDeviceIds()); - // TODO @li:只需要传递 deviceIds、firewareId、剩余的 upgradeRecordService 里面自己处理;这样,后续 record 加字段,都不需要透传太多;解耦 - upgradeRecordService.createUpgradeRecordBatch(upgradeRecordList); - // TODO @芋艿: 创建任务触发的其他Action + upgradeRecordService.createOtaUpgradeRecordBatch(upgradeTask.getDeviceIds(), firmware.getId(), upgradeTask.getId()); return upgradeTask.getId(); } @@ -76,16 +68,17 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { // 1.1 校验升级任务是否存在 IotOtaUpgradeTaskDO upgradeTask = validateUpgradeTaskExists(id); // 1.2 校验升级任务是否可以取消 - // TODO @li:这种一次性的,可以不考虑拆分方法 - validateUpgradeTaskCanCancel(upgradeTask); - + // 检查升级任务的状态是否为进行中,只有此状态下的任务才允许取消 + if (!Objects.equals(upgradeTask.getStatus(), IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus())) { + // 只有进行中的任务才可以取消 + throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL); + } // 2. 更新 OTA 升级任务状态为已取消 upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder() .id(id).status(IotOtaUpgradeTaskStatusEnum.CANCELED.getStatus()) .build()); // 3. 更新 OTA 升级记录状态为已取消 upgradeRecordService.cancelUpgradeRecordByTaskId(id); - // TODO @芋艿: 取消任务触发的其他Action } @Override @@ -131,19 +124,27 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { } /** - * 验证升级任务的范围和设备参数是否有效 - * 当选择特定设备进行升级时,确保提供的设备ID和设备名称列表有效且对应 + * 验证升级任务的范围和设备列表的有效性。 + *

+ * 根据升级任务的范围(scope),验证设备列表(deviceIds)或产品ID(productId)是否有效。 + * 如果范围是“选择设备”(SELECT),则必须提供设备列表;如果范围是“所有设备”(ALL),则必须根据产品ID获取设备列表,并确保列表不为空。 * - * @param scope 升级任务的范围,表示是选择特定设备还是其他范围 - * @param deviceIds 设备ID列表,用于标识参与升级的设备 - * @param deviceNames 设备名称列表,与设备ID列表对应 + * @param scope 升级任务的范围,参考 IotOtaUpgradeTaskScopeEnum 枚举值 + * @param deviceIds 设备ID列表,当范围为“选择设备”时,该列表不能为空 + * @param productId 产品ID,当范围为“所有设备”时,用于获取设备列表 + * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,抛出相应的异常 */ - private void validateScopeAndDevice(Integer scope, List deviceIds, List deviceNames) { - // 当升级任务范围为选择特定设备时 + private void validateScopeAndDevice(Integer scope, List deviceIds, String productId) { + // 验证范围为“选择设备”时,设备列表不能为空 if (Objects.equals(scope, IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { - // 检查设备ID列表和设备名称列表是否为空或长度不一致,若不符合要求,则抛出异常 - if (CollUtil.isEmpty(deviceIds) || CollUtil.isEmpty(deviceNames) || deviceIds.size() != deviceNames.size()) { - throw exception(OTA_UPGRADE_TASK_PARAMS_INVALID); + if (CollUtil.isEmpty(deviceIds)) { + throw exception(OTA_UPGRADE_TASK_DEVICE_IDS_EMPTY); + } + } else if (Objects.equals(scope, IotOtaUpgradeTaskScopeEnum.ALL.getScope())) { + // 验证范围为“所有设备”时,根据产品ID获取的设备列表不能为空 + List deviceList = deviceService.getDeviceListByProductId(Convert.toLong(productId)); + if (CollUtil.isEmpty(deviceList)) { + throw exception(OTA_UPGRADE_TASK_DEVICE_LIST_EMPTY); } } } @@ -166,25 +167,6 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { return upgradeTask; } - /** - * 验证升级任务是否可以被取消 - *

- * 此方法旨在确保只有当升级任务处于进行中状态时,才可以执行取消操作 - * 它通过比较任务的当前状态与预定义的进行中状态来判断是否允许取消操作 - * 如果任务状态不符合条件,则抛出异常,表明该任务无法取消 - * - * @param upgradeTask 待验证的升级任务对象,包含任务的详细信息,如状态等 - * @throws cn.iocoder.yudao.framework.common.exception.ServiceException 如果任务状态不是进行中,则抛出此异常,表明任务无法取消 - */ - private void validateUpgradeTaskCanCancel(IotOtaUpgradeTaskDO upgradeTask) { - // 检查升级任务的状态是否为进行中,只有此状态下的任务才允许取消 - if (!Objects.equals(upgradeTask.getStatus(), IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus())) { - // 只有进行中的任务才可以取消 - throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL); - } - } - - // TODO @li:一次性,不复用的,可以直接写在对应的逻辑里; /** * 初始化升级任务 *

@@ -195,55 +177,29 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { * @param createReqVO 升级任务保存请求对象,包含创建升级任务所需的信息 * @return 返回初始化后的升级任务对象 */ - private IotOtaUpgradeTaskDO initUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO, String productId) { - // 配置各项参数 - IotOtaUpgradeTaskDO upgradeTask = IotOtaUpgradeTaskDO.builder() - // TODO @li:不用每个占一行。最好相同类型的,放在一行里; - .name(createReqVO.getName()) - .description(createReqVO.getDescription()) - .firmwareId(createReqVO.getFirmwareId()) - .scope(createReqVO.getScope()) - .deviceIds(createReqVO.getDeviceIds()) - .deviceNames(createReqVO.getDeviceNames()) - .deviceCount(Convert.toLong(CollUtil.size(createReqVO.getDeviceIds()))) - .status(IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus()) - .build(); + private IotOtaUpgradeTaskDO initOtaUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO, String productId) { + // 将请求参数转换为升级任务对象 + IotOtaUpgradeTaskDO upgradeTask = BeanUtils.toBean(createReqVO, IotOtaUpgradeTaskDO.class); + // 初始化的时候,设置设备数量和状态 + upgradeTask.setDeviceCount(Convert.toLong(CollUtil.size(createReqVO.getDeviceIds()))) + .setStatus(IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus()); // 如果选择全选,则需要查询设备数量 if (Objects.equals(createReqVO.getScope(), IotOtaUpgradeTaskScopeEnum.ALL.getScope())) { // 根据产品ID查询设备数量 - Long deviceCount = deviceService.getDeviceCountByProductId(Convert.toLong(productId)); + List deviceList = deviceService.getDeviceListByProductId(Convert.toLong(productId)); // 设置升级任务的设备数量 - upgradeTask.setDeviceCount(deviceCount); + upgradeTask.setDeviceCount((long) deviceList.size()); + upgradeTask.setDeviceIds( + deviceList.stream().map(IotDeviceDO::getId).collect(Collectors.toList())); + upgradeTask.setDeviceNames( + deviceList.stream().map(IotDeviceDO::getDeviceName).collect(Collectors.toList())); + } else if (Objects.equals(createReqVO.getScope(), IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { + List deviceList = deviceService.getDeviceListByIdList(createReqVO.getDeviceIds()); + upgradeTask.setDeviceNames( + deviceList.stream().map(IotDeviceDO::getDeviceName).collect(Collectors.toList())); } // 返回初始化后的升级任务对象 return upgradeTask; } - /** - * 初始化升级记录列表 - *

- * 根据升级任务的范围(选择设备或按产品ID)获取设备列表,并将其转换为升级记录请求对象列表。 - * - * @param upgradeTask 升级任务对象,包含升级任务的相关信息 - * @param firmware 固件对象,包含固件的相关信息 - * @param deviceIds 设备ID列表,仅在升级任务范围为选择设备时使用 - * @return 升级记录请求对象列表,包含每个设备的升级记录信息 - */ - private List initUpgradeRecordList( - IotOtaUpgradeTaskDO upgradeTask, IotOtaFirmwareDO firmware, List deviceIds) { - // TODO @li:需要考虑,如果创建多个任务,相互之间不能重复; - // 1)指定设备的时候,进行校验;2)如果是全部,则过滤其它已经发起的;;;;;另外,需要排除掉 cancel 的哈。因为 cancal 之后,还可以发起 - // 根据升级任务的范围确定设备列表 - List deviceList; - if (Objects.equals(upgradeTask.getScope(), IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { - // 如果升级任务范围为选择设备,则根据设备ID列表获取设备信息 - deviceList = deviceService.getDeviceListByIdList(deviceIds); - } else { - // 如果升级任务范围为按产品ID,则根据固件的产品ID获取设备信息 - deviceList = deviceService.getDeviceListByProductId(Convert.toLong(firmware.getProductId())); - } - // 将升级任务、固件和设备列表转换为升级记录请求对象列表 - return IotOtaUpgradeRecordConvert.INSTANCE.convertBOList(upgradeTask, firmware, deviceList); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java deleted file mode 100644 index 2567be5294..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordCreateReqBO.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota.bo; - -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; -import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.time.LocalDateTime; - -@Data -public class IotOtaUpgradeRecordCreateReqBO { - - /** - * 固件编号 - *

- * 关联 {@link IotOtaFirmwareDO#getId()} - */ - @NotNull(message = "固件编号不能为空") - private Long firmwareId; - /** - * 任务编号 - *

- * 关联 {@link IotOtaUpgradeTaskDO#getId()} - */ - @NotNull(message = "任务编号不能为空") - private Long taskId; - /** - * 产品标识 - *

- * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} - */ - private String productKey; - /** - * 设备名称 - *

- * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} - */ - private String deviceName; - /** - * 设备编号 - *

- * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()} - */ - @NotNull(message = "设备编号不能为空") - private String deviceId; - /** - * 来源的固件编号 - *

- * 关联 {@link IotDeviceDO#getFirmwareId()} - */ - private Long fromFirmwareId; - /** - * 升级状态 - *

- * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} - */ - private Integer status; - /** - * 升级进度,百分比 - */ - private Integer progress; - /** - * 升级进度描述 - *

- * 注意,只记录设备最后一次的升级进度描述 - * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志 - */ - private String description; - /** - * 升级开始时间 - */ - private LocalDateTime startTime; - /** - * 升级结束时间 - */ - private LocalDateTime endTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java deleted file mode 100644 index ac73aa884e..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/bo/IotOtaUpgradeRecordUpdateReqBO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.iot.service.ota.bo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.hibernate.validator.constraints.Range; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -// TODO @li:这个类,貌似没用? -@Data -public class IotOtaUpgradeRecordUpdateReqBO { - - /** - * 升级记录编号 - */ - @NotNull(message = "升级记录编号不能为空") - private Long id; - /** - * 升级状态 - *

- * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum} - */ - @InEnum(IotOtaUpgradeRecordStatusEnum.class) - private Integer status; - /** - * 升级进度,百分比 - */ - @Range(min = 0, max = 100, message = "升级进度必须介于 0-100 之间") - private Integer progress; - /** - * 升级开始时间 - */ - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime startTime; - /** - * 升级结束时间 - */ - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime endTime; - -} From 37c725c1a3d7970fdd8f9c35b02a12439725a092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E7=8E=84=E7=A4=BC?= Date: Thu, 13 Mar 2025 22:23:28 +0800 Subject: [PATCH 271/309] =?UTF-8?q?refactor(iot):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E4=BB=BB=E5=8A=A1=E4=B8=AD=E7=9A=84=E8=AE=BE?= =?UTF-8?q?=E5=A4=87=E5=90=8D=E7=A7=B0=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除了创建升级任务时设置设备名称的代码逻辑 - 优化了升级任务初始化过程,减少了不必要的设备名称查询和设置操作 --- .../iot/service/ota/IotOtaUpgradeTaskServiceImpl.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java index d20f27e8cf..ca74481ff2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java @@ -191,12 +191,6 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { upgradeTask.setDeviceCount((long) deviceList.size()); upgradeTask.setDeviceIds( deviceList.stream().map(IotDeviceDO::getId).collect(Collectors.toList())); - upgradeTask.setDeviceNames( - deviceList.stream().map(IotDeviceDO::getDeviceName).collect(Collectors.toList())); - } else if (Objects.equals(createReqVO.getScope(), IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { - List deviceList = deviceService.getDeviceListByIdList(createReqVO.getDeviceIds()); - upgradeTask.setDeviceNames( - deviceList.stream().map(IotDeviceDO::getDeviceName).collect(Collectors.toList())); } // 返回初始化后的升级任务对象 return upgradeTask; From ffe4afaaafea38e1c4ca54f1f64025b63fd23ac7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 14 Mar 2025 13:26:23 +0800 Subject: [PATCH 272/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=20function=20ca?= =?UTF-8?q?ll=20=E7=A4=BA=E4=BE=8B=EF=BC=8C=E5=AE=8C=E6=88=90=E6=89=80?= =?UTF-8?q?=E6=9C=89=E6=A8=A1=E5=9E=8B=E7=9A=84=E6=B5=8B=E8=AF=95=20=3D=20?= =?UTF-8?q?=3D=20=E7=B4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat/AiChatMessageServiceImpl.java | 4 +- .../module/ai/service/tool/ListDirTool.java | 95 -------------- .../DirectoryListToolFunction.java} | 29 +++-- .../function/WeatherQueryToolFunction.java | 118 ++++++++++++++++++ .../module/ai/service/tool/package-info.java | 1 + .../ai/config/YudaoAiAutoConfiguration.java | 11 ++ .../ai/core/factory/AiModelFactoryImpl.java | 29 +++-- .../yudao/framework/ai/core/util/AiUtils.java | 34 ++--- 8 files changed, 186 insertions(+), 135 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/{ListDirToolB.java => function/DirectoryListToolFunction.java} (74%) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/WeatherQueryToolFunction.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 88f7144336..877ba77453 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.util.AiUtils; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; @@ -238,7 +239,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { // 2. 构建 ChatOptions 对象 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), - conversation.getTemperature(), conversation.getMaxTokens()); + conversation.getTemperature(), conversation.getMaxTokens(), + SetUtils.asSet("directory_list", "weather_query")); return new Prompt(chatMessages, chatOptions); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java deleted file mode 100644 index 4315e549ae..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.tool; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.ai.tool.annotation.Tool; -import org.springframework.ai.tool.annotation.ToolParam; -import org.springframework.stereotype.Component; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 目录内容列表工具:列出指定目录的内容 - * - * @author 芋道源码 - */ -@Component -public class ListDirTool { - - /** - * 列出指定目录的内容 - * - * @param relativePath 要列出内容的目录路径,相对于工作区根目录 - * @return 目录内容列表 - */ - @Tool(name = "listDir", description = "列出指定目录的内容") - public Response listDir(@ToolParam(description = "要列出内容的目录路径,相对于工作区根目录") String relativePath) { - // 校验目录存在 - String path = StrUtil.blankToDefault(relativePath, "."); - Path dirPath = Paths.get(path); - if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { - return new Response(Collections.emptyList()); - } - // 列出目录内容 - File[] files = dirPath.toFile().listFiles(); - if (ArrayUtil.isEmpty(files)) { - return new Response(Collections.emptyList()); - } - return new Response(convertList(Arrays.asList(files), file -> new Response.File() - .setDirectory(file.isDirectory()).setName(file.getName()) - .setSize(file.isFile() ? FileUtil.readableFileSize(file.length()) : null) - .setLastModified( - LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN)))); - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class Response { - - /** - * 目录内容列表 - */ - private List files; - - @Data - public static class File { - - /** - * 是否为目录 - */ - private Boolean directory; - - /** - * 名称 - */ - private String name; - - /** - * 大小,仅对文件有效 - */ - private String size; - - /** - * 最后修改时间 - */ - private String lastModified; - - } - - } - -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java similarity index 74% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java index 3eafff4b2c..0ec86724ec 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java @@ -1,10 +1,11 @@ -package cn.iocoder.yudao.module.ai.service.tool; +package cn.iocoder.yudao.module.ai.service.tool.function; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.annotation.JsonClassDescription; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import lombok.AllArgsConstructor; import lombok.Data; @@ -12,8 +13,6 @@ import lombok.NoArgsConstructor; import org.springframework.stereotype.Component; import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -23,21 +22,22 @@ import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; /** - * 目录内容列表工具:列出指定目录的内容 + * 工具:列出指定目录的文件列表 * * @author 芋道源码 */ -@Component("listDir") -public class ListDirToolB implements Function { +@Component("directory_list") +public class DirectoryListToolFunction implements Function { @Data - @JsonClassDescription("列出指定目录的内容") + @JsonClassDescription("列出指定目录的文件列表") public static class Request { /** - * 要列出内容的目录路径 + * 目录路径 */ - @JsonPropertyDescription("要列出内容的目录路径,例如说:/Users/yunai") + @JsonProperty(required = true, value = "path") + @JsonPropertyDescription("目录路径,例如说:/Users/yunai") private String path; } @@ -48,7 +48,7 @@ public class ListDirToolB implements Function files; @@ -82,13 +82,12 @@ public class ListDirToolB implements Function { + + private static final String[] WEATHER_CONDITIONS = { "晴朗", "多云", "阴天", "小雨", "大雨", "雷雨", "小雪", "大雪" }; + + @Data + @JsonClassDescription("查询指定城市的天气信息") + public static class Request { + + /** + * 城市名称 + */ + @JsonProperty(required = true, value = "city") + @JsonPropertyDescription("城市名称,例如:北京、上海、广州") + private String city; + + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Response { + + /** + * 城市名称 + */ + private String city; + + /** + * 天气信息 + */ + private WeatherInfo weatherInfo; + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class WeatherInfo { + + /** + * 温度(摄氏度) + */ + private Integer temperature; + + /** + * 天气状况 + */ + private String condition; + + /** + * 湿度百分比 + */ + private Integer humidity; + + /** + * 风速(km/h) + */ + private Integer windSpeed; + + /** + * 查询时间 + */ + private String queryTime; + + } + + } + + @Override + public Response apply(Request request) { + // 检查城市名称是否为空 + if (StrUtil.isBlank(request.getCity())) { + return new Response("未知城市", null); + } + + // 获取天气数据 + String city = request.getCity(); + Response.WeatherInfo weatherInfo = generateMockWeatherInfo(); + return new Response(city, weatherInfo); + } + + /** + * 生成模拟的天气数据 + * 在实际应用中,应替换为真实 API 调用 + */ + private Response.WeatherInfo generateMockWeatherInfo() { + int temperature = RandomUtil.randomInt(-5, 30); + int humidity = RandomUtil.randomInt(1, 100); + int windSpeed = RandomUtil.randomInt(1, 30); + String condition = RandomUtil.randomEle(WEATHER_CONDITIONS); + return new Response.WeatherInfo(temperature, condition, humidity, windSpeed, + LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN)); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java new file mode 100644 index 0000000000..ba65093ecc --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.ai.service.tool; \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java index 720d933087..ef3314a48b 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.framework.ai.config; import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; @@ -17,6 +18,7 @@ import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStore import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.TokenCountBatchingStrategy; +import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.openai.OpenAiChatModel; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.openai.api.OpenAiApi; @@ -70,6 +72,7 @@ public class YudaoAiAutoConfiguration { .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) + .toolCallingManager(getToolCallingManager()) .build(); return new DeepSeekChatModel(openAiChatModel); } @@ -96,6 +99,7 @@ public class YudaoAiAutoConfiguration { .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) + .toolCallingManager(getToolCallingManager()) .build(); return new DouBaoChatModel(openAiChatModel); } @@ -122,6 +126,7 @@ public class YudaoAiAutoConfiguration { .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) + .toolCallingManager(getToolCallingManager()) .build(); return new SiliconFlowChatModel(openAiChatModel); } @@ -155,6 +160,7 @@ public class YudaoAiAutoConfiguration { .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) + .toolCallingManager(getToolCallingManager()) .build(); return new HunYuanChatModel(openAiChatModel); } @@ -181,6 +187,7 @@ public class YudaoAiAutoConfiguration { .maxTokens(properties.getMaxTokens()) .topP(properties.getTopP()) .build()) + .toolCallingManager(getToolCallingManager()) .build(); return new XingHuoChatModel(openAiChatModel); } @@ -210,4 +217,8 @@ public class YudaoAiAutoConfiguration { return new TokenCountBatchingStrategy(); } + private static ToolCallingManager getToolCallingManager() { + return SpringUtil.getBean(ToolCallingManager.class); + } + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java index 8565294b0c..356715be26 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java @@ -23,6 +23,7 @@ import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; @@ -58,11 +59,14 @@ import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.image.ImageModel; import org.springframework.ai.minimax.MiniMaxChatModel; +import org.springframework.ai.minimax.MiniMaxChatOptions; import org.springframework.ai.minimax.MiniMaxEmbeddingModel; import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; import org.springframework.ai.minimax.api.MiniMaxApi; +import org.springframework.ai.model.function.FunctionCallbackResolver; import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.moonshot.MoonshotChatModel; +import org.springframework.ai.moonshot.MoonshotChatOptions; import org.springframework.ai.moonshot.api.MoonshotApi; import org.springframework.ai.ollama.OllamaChatModel; import org.springframework.ai.ollama.OllamaEmbeddingModel; @@ -90,10 +94,7 @@ import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservat import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; import org.springframework.ai.vectorstore.redis.RedisVectorStore; -import org.springframework.ai.zhipuai.ZhiPuAiChatModel; -import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel; -import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions; -import org.springframework.ai.zhipuai.ZhiPuAiImageModel; +import org.springframework.ai.zhipuai.*; import org.springframework.ai.zhipuai.api.ZhiPuAiApi; import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; import org.springframework.beans.BeansException; @@ -110,6 +111,7 @@ import java.util.Timer; import java.util.TimerTask; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE; /** * AI Model 模型工厂的实现类 @@ -308,7 +310,9 @@ public class AiModelFactoryImpl implements AiModelFactory { */ private static DashScopeChatModel buildTongYiChatModel(String key) { DashScopeApi dashScopeApi = new DashScopeApi(key); - return new DashScopeChatModel(dashScopeApi); + DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) + .withTemperature(0.7).build(); + return new DashScopeChatModel(dashScopeApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); } /** @@ -385,7 +389,8 @@ public class AiModelFactoryImpl implements AiModelFactory { private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) : new ZhiPuAiApi(url, apiKey); - return new ZhiPuAiChatModel(zhiPuAiApi); + ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); + return new ZhiPuAiChatModel(zhiPuAiApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); } /** @@ -403,7 +408,8 @@ public class AiModelFactoryImpl implements AiModelFactory { private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) : new MiniMaxApi(url, apiKey); - return new MiniMaxChatModel(miniMaxApi); + MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); + return new MiniMaxChatModel(miniMaxApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); } /** @@ -412,7 +418,8 @@ public class AiModelFactoryImpl implements AiModelFactory { private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) : new MoonshotApi(url, apiKey); - return new MoonshotChatModel(moonshotApi); + MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); + return new MoonshotChatModel(moonshotApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); } /** @@ -449,7 +456,7 @@ public class AiModelFactoryImpl implements AiModelFactory { // 获取 AzureOpenAiChatProperties 对象 AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, - null, null, null); + getToolCallingManager(), null, null); } /** @@ -704,4 +711,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(ToolCallingManager.class); } + private static FunctionCallbackResolver getFunctionCallbackResolver() { + return SpringUtil.getBean(FunctionCallbackResolver.class); + } + } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index b15fb2e54d..becc54ee43 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -13,6 +13,8 @@ import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.qianfan.QianFanChatOptions; import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; +import java.util.Set; + /** * Spring AI 工具类 * @@ -21,22 +23,27 @@ import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; public class AiUtils { public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { + return buildChatOptions(platform, model, temperature, maxTokens, null); + } + + public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, + Set toolNames) { // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: - // TODO functions - return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens).build(); + return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) + .withFunctions(toolNames).build(); case YI_YAN: return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); case ZHI_PU: - // TODO functions - return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); + return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) + .functions(toolNames).build(); case MINI_MAX: - // TODO functions - return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); + return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) + .functions(toolNames).build(); case MOONSHOT: - // TODO functions - return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); + return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) + .functions(toolNames).build(); case OPENAI: case DEEP_SEEK: // 复用 OpenAI 客户端 case DOU_BAO: // 复用 OpenAI 客户端 @@ -44,17 +51,14 @@ public class AiUtils { case XING_HUO: // 复用 OpenAI 客户端 case SILICON_FLOW: // 复用 OpenAI 客户端 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) -// .toolNames() TODO - .toolNames("listDir") - .build(); + .toolNames(toolNames).build(); case AZURE_OPENAI: // TODO 芋艿:貌似没 model 字段???! - // TODO 芋艿:.toolNames() TODO - return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens).build(); + return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) + .toolNames(toolNames).build(); case OLLAMA: - // TODO 芋艿:.toolNames() TODO return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) - .toolNames("listDir").build(); + .toolNames(toolNames).build(); default: throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); } From 966357b44e4db079d2db207254148e62463c035c Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 14 Mar 2025 17:34:16 +0800 Subject: [PATCH 273/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81=E6=8A=BD=E7=A6=BB=E5=88=B0?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/IotDataBridgeAbstractConfig.java | 12 +- .../databridge/IotDataBridgeExecute.java | 1 - .../IotKafkaMQDataBridgeExecute.java | 36 ---- .../IotRabbitMQDataBridgeExecute.java | 40 ----- .../IotRedisStreamMQDataBridgeExecute.java | 39 +---- .../IotRocketMQDataBridgeExecute.java | 37 ---- .../databridge/IotDataBridgeExecuteTest.java | 159 ++++++++++++++++++ yudao-server/pom.xml | 25 ++- 8 files changed, 178 insertions(+), 171 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java index 06219f7236..550550d195 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java @@ -15,12 +15,12 @@ import lombok.Data; @Data @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) @JsonSubTypes({ - @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "HTTP"), - @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "KAFKA"), - @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "MQTT"), - @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "RABBITMQ"), - @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "REDIS_STREAM"), - @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "ROCKETMQ"), + @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "1"), + @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "10"), + @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "21"), + @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "30"), + @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "31"), + @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "32"), }) public abstract class IotDataBridgeAbstractConfig { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java index 1e8d939ec2..ce3d0f1938 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecute.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; -import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 3b7f99bf42..5674c7d609 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeKafkaMQConfig; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.extern.slf4j.Slf4j; @@ -13,7 +12,6 @@ import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Component; import java.time.Duration; -import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -77,38 +75,4 @@ public class IotKafkaMQDataBridgeExecute extends producer.destroy(); } - // TODO @芋艿:测试代码,后续清理 - public static void main(String[] args) { - // 1. 创建一个共享的实例 - IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute(); - - // 2. 创建共享的配置 - IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig(); - config.setBootstrapServers("127.0.0.1:9092"); - config.setTopic("test-topic"); - config.setSsl(false); - config.setUsername(null); - config.setPassword(null); - - // 3. 创建共享的消息 - IotDeviceMessage message = IotDeviceMessage.builder() - .requestId("TEST-001") - .productKey("testProduct") - .deviceName("testDevice") - .deviceKey("testDeviceKey") - .type("property") - .identifier("temperature") - .data("{\"value\": 60}") - .reportTime(LocalDateTime.now()) - .tenantId(1L) - .build(); - - // 4. 执行两次测试,验证缓存 - log.info("[main][第一次执行,应该会创建新的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - - log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java index 54485c091d..efe08b1fcb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRabbitMQDataBridgeExecute.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRabbitMQConfig; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import com.rabbitmq.client.Channel; @@ -12,7 +11,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; /** * RabbitMQ 的 {@link IotDataBridgeExecute} 实现类 @@ -76,42 +74,4 @@ public class IotRabbitMQDataBridgeExecute extends } } - // TODO @芋艿:测试代码,后续清理 - public static void main(String[] args) { - // 1. 创建一个共享的实例 - IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute(); - - // 2. 创建共享的配置 - IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig(); - config.setHost("localhost"); - config.setPort(5672); - config.setVirtualHost("/"); - config.setUsername("admin"); - config.setPassword("123456"); - config.setExchange("test-exchange"); - config.setRoutingKey("test-key"); - config.setQueue("test-queue"); - - // 3. 创建共享的消息 - IotDeviceMessage message = IotDeviceMessage.builder() - .requestId("TEST-001") - .productKey("testProduct") - .deviceName("testDevice") - .deviceKey("testDeviceKey") - .type("property") - .identifier("temperature") - .data("{\"value\": 60}") - .reportTime(LocalDateTime.now()) - .tenantId(1L) - .build(); - - // 4. 执行两次测试,验证缓存 - // 4. 执行两次测试,验证缓存 - log.info("[main][第一次执行,应该会创建新的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - - log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java index 5616c7e648..2aac76619a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRedisStreamMQDataBridgeExecute.java @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRedisStreamMQConfig; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import com.fasterxml.jackson.databind.ObjectMapper; @@ -21,8 +20,6 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; - /** * Redis Stream MQ 的 {@link IotDataBridgeExecute} 实现类 * @@ -96,38 +93,4 @@ public class IotRedisStreamMQDataBridgeExecute extends return json; } - // TODO @芋艿:测试代码,后续清理 - public static void main(String[] args) { - // 1. 创建一个共享的实例 - IotRedisStreamMQDataBridgeExecute action = new IotRedisStreamMQDataBridgeExecute(); - - // 2. 创建共享的配置 - IotDataBridgeRedisStreamMQConfig config = new IotDataBridgeRedisStreamMQConfig(); - config.setHost("127.0.0.1"); - config.setPort(6379); - config.setDatabase(0); - config.setPassword("123456"); - config.setTopic("test-stream"); - - // 3. 创建共享的消息 - IotDeviceMessage message = IotDeviceMessage.builder() - .requestId("TEST-001") - .productKey("testProduct") - .deviceName("testDevice") - .deviceKey("testDeviceKey") - .type("property") - .identifier("temperature") - .data("{\"value\": 60}") - .reportTime(LocalDateTime.now()) - .tenantId(1L) - .build(); - - // 4. 执行两次测试,验证缓存 - log.info("[main][第一次执行,应该会创建新的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - - log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - } - -} \ No newline at end of file +} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java index c3e729dda3..d3ac77227a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotRocketMQDataBridgeExecute.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.service.rule.action.databridge; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeRocketMQConfig; -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import lombok.extern.slf4j.Slf4j; @@ -13,8 +12,6 @@ import org.apache.rocketmq.remoting.common.RemotingHelper; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.stereotype.Component; -import java.time.LocalDateTime; - /** * RocketMQ 的 {@link IotDataBridgeExecute} 实现类 * @@ -65,38 +62,4 @@ public class IotRocketMQDataBridgeExecute extends producer.shutdown(); } - // TODO @芋艿:测试代码,后续清理 - // TODO @puhui999:搞到测试类里。 - public static void main(String[] args) { - // 1. 创建一个共享的实例 - IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); - - // 2. 创建共享的配置 - IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig(); - config.setNameServer("127.0.0.1:9876"); - config.setGroup("test-group"); - config.setTopic("test-topic"); - config.setTags("test-tag"); - - // 3. 创建共享的消息 - IotDeviceMessage message = IotDeviceMessage.builder() - .requestId("TEST-001") - .productKey("testProduct") - .deviceName("testDevice") - .deviceKey("testDeviceKey") - .type("property") - .identifier("temperature") - .data("{\"value\": 60}") - .reportTime(LocalDateTime.now()) - .tenantId(1L) - .build(); - - // 4. 执行两次测试,验证缓存 - log.info("[main][第一次执行,应该会创建新的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - - log.info("[main][第二次执行,应该会复用缓存的 producer]"); - action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); - } - } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java new file mode 100644 index 0000000000..03ea33d682 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java @@ -0,0 +1,159 @@ +package cn.iocoder.yudao.module.iot.service.rule.action.databridge; + +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO; +import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; + +/** + * {@link IotDataBridgeExecute} 实现类的测试 + * + * @author HUIHUI + */ +@Disabled // 默认禁用,需要手动启用测试 +@Slf4j +public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { + + private IotDeviceMessage message; + + @Mock + private RestTemplate restTemplate; + + @InjectMocks + private IotHttpDataBridgeExecute httpDataBridgeExecute; + + @BeforeEach + public void setUp() { + // 创建共享的测试消息 + message = IotDeviceMessage.builder() + .requestId("TEST-001") + .productKey("testProduct") + .deviceName("testDevice") + .deviceKey("testDeviceKey") + .type("property") + .identifier("temperature") + .data("{\"value\": 60}") + .reportTime(LocalDateTime.now()) + .tenantId(1L) + .build(); + + // 配置 RestTemplate mock 返回成功响应 + Mockito.when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class))) + .thenReturn(new ResponseEntity<>("Success", HttpStatus.OK)); + } + + @Test + public void testKafkaMQDataBridge() { + // 1. 创建执行器实例 + IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute(); + + // 2. 创建配置 + IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig(); + config.setBootstrapServers("127.0.0.1:9092"); + config.setTopic("test-topic"); + config.setSsl(false); + config.setUsername(null); + config.setPassword(null); + + // 3. 执行两次测试,验证缓存 + log.info("[testKafkaMQDataBridge][第一次执行,应该会创建新的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + + log.info("[testKafkaMQDataBridge][第二次执行,应该会复用缓存的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + } + + @Test + public void testRabbitMQDataBridge() { + // 1. 创建执行器实例 + IotRabbitMQDataBridgeExecute action = new IotRabbitMQDataBridgeExecute(); + + // 2. 创建配置 + IotDataBridgeRabbitMQConfig config = new IotDataBridgeRabbitMQConfig(); + config.setHost("localhost"); + config.setPort(5672); + config.setVirtualHost("/"); + config.setUsername("admin"); + config.setPassword("123456"); + config.setExchange("test-exchange"); + config.setRoutingKey("test-key"); + config.setQueue("test-queue"); + + // 3. 执行两次测试,验证缓存 + log.info("[testRabbitMQDataBridge][第一次执行,应该会创建新的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + + log.info("[testRabbitMQDataBridge][第二次执行,应该会复用缓存的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + } + + @Test + public void testRedisStreamMQDataBridge() { + // 1. 创建执行器实例 + IotRedisStreamMQDataBridgeExecute action = new IotRedisStreamMQDataBridgeExecute(); + + // 2. 创建配置 + IotDataBridgeRedisStreamMQConfig config = new IotDataBridgeRedisStreamMQConfig(); + config.setHost("127.0.0.1"); + config.setPort(6379); + config.setDatabase(0); + config.setPassword("123456"); + config.setTopic("test-stream"); + + // 3. 执行两次测试,验证缓存 + log.info("[testRedisStreamMQDataBridge][第一次执行,应该会创建新的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + + log.info("[testRedisStreamMQDataBridge][第二次执行,应该会复用缓存的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + } + + @Test + public void testRocketMQDataBridge() { + // 1. 创建执行器实例 + IotRocketMQDataBridgeExecute action = new IotRocketMQDataBridgeExecute(); + + // 2. 创建配置 + IotDataBridgeRocketMQConfig config = new IotDataBridgeRocketMQConfig(); + config.setNameServer("127.0.0.1:9876"); + config.setGroup("test-group"); + config.setTopic("test-topic"); + config.setTags("test-tag"); + + // 3. 执行两次测试,验证缓存 + log.info("[testRocketMQDataBridge][第一次执行,应该会创建新的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + + log.info("[testRocketMQDataBridge][第二次执行,应该会复用缓存的 producer]"); + action.execute(message, new IotDataBridgeDO().setType(action.getType()).setConfig(config)); + } + + @Test + public void testHttpDataBridge() throws Exception { + // 创建配置 + IotDataBridgeHttpConfig config = new IotDataBridgeHttpConfig(); + config.setUrl("https://doc.iocoder.cn/"); + config.setMethod(HttpMethod.GET.name()); + + // 执行测试 + log.info("[testHttpDataBridge][执行HTTP数据桥接测试]"); + httpDataBridgeExecute.execute(message, new IotDataBridgeDO().setType(httpDataBridgeExecute.getType()).setConfig(config)); + } + +} diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index f408ca3e5d..0251e7b649 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -115,19 +115,18 @@ ${revision} - - - org.apache.rocketmq - rocketmq-spring-boot-starter - - - org.springframework.kafka - spring-kafka - - - org.springframework.boot - spring-boot-starter-amqp - + + + + + + + + + + + + From 3756830b9c672f2a035b6aa0a29f2a8b1fbb6cac Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 14 Mar 2025 17:40:19 +0800 Subject: [PATCH 274/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/rule/IotDataBridgeController.java | 22 ------------------- .../vo/databridge/IotDataBridgePageReqVO.java | 11 ---------- .../vo/databridge/IotDataBridgeRespVO.java | 18 --------------- .../vo/databridge/IotDataBridgeSaveReqVO.java | 8 ++++++- .../dal/dataobject/rule/IotDataBridgeDO.java | 3 ++- 5 files changed, 9 insertions(+), 53 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java index f6ba161234..95e50a4a27 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java @@ -1,11 +1,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule; -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeRespVO; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO; @@ -15,16 +12,11 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @Tag(name = "管理后台 - IoT 数据桥梁") @@ -77,18 +69,4 @@ public class IotDataBridgeController { return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class)); } - // TODO @puhui999:不用导出哈。相关的 IotDataBridgeRespVO 里的导出也注释掉哈 - @GetMapping("/export-excel") - @Operation(summary = "导出数据桥梁 Excel") - @PreAuthorize("@ss.hasPermission('iot:data-bridge:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportDataBridgeExcel(@Valid IotDataBridgePageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = dataBridgeService.getDataBridgePage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "IoT 数据桥梁.xls", "数据", IotDataBridgeRespVO.class, - BeanUtils.toBean(list, IotDataBridgeRespVO.class)); - } - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java index a3f8007c31..401f796f6f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java @@ -20,22 +20,11 @@ public class IotDataBridgePageReqVO extends PageParam { @Schema(description = "桥梁名称", example = "赵六") private String name; - // TODO @puhui999:description、direction、type 不过滤哈 - @Schema(description = "桥梁描述", example = "随便") - private String description; - @Schema(description = "桥梁状态", example = "2") private Integer status; - @Schema(description = "桥梁方向") - private Integer direction; - - @Schema(description = "桥梁类型", example = "1") - private Integer type; - @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; - } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java index a8faf40d60..1db10a762d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java @@ -1,57 +1,39 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.IOT_DATA_BRIDGE_DIRECTION_ENUM; -import static cn.iocoder.yudao.module.iot.enums.DictTypeConstants.IOT_DATA_BRIDGE_TYPE_ENUM; -import static cn.iocoder.yudao.module.system.enums.DictTypeConstants.COMMON_STATUS; - @Schema(description = "管理后台 - IoT 数据桥梁 Response VO") @Data @ExcelIgnoreUnannotated public class IotDataBridgeRespVO { @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") - @ExcelProperty("桥梁编号") private Long id; @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @ExcelProperty("桥梁名称") private String name; @Schema(description = "桥梁描述", example = "随便") - @ExcelProperty("桥梁描述") private String description; @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @ExcelProperty(value = "桥梁状态", converter = DictConvert.class) - @DictFormat(COMMON_STATUS) private Integer status; @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty(value = "桥梁方向", converter = DictConvert.class) - @DictFormat(IOT_DATA_BRIDGE_DIRECTION_ENUM) private Integer direction; @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "桥梁类型", converter = DictConvert.class) - @DictFormat(IOT_DATA_BRIDGE_TYPE_ENUM) private Integer type; @Schema(description = "桥梁配置") - @ExcelProperty("桥梁配置") private IotDataBridgeAbstractConfig config; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") private LocalDateTime createTime; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java index 37dc9b218f..8441701af8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; @@ -22,15 +26,17 @@ public class IotDataBridgeSaveReqVO { @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") @NotNull(message = "桥梁状态不能为空") + @InEnum(CommonStatusEnum.class) private Integer status; - // TODO @puhui999:枚举的校验 @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) @NotNull(message = "桥梁方向不能为空") + @InEnum(IotDataBridgeDirectionEnum.class) private Integer direction; @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "桥梁类型不能为空") + @InEnum(IotDataBridgeTypeEnum.class) private Integer type; @Schema(description = "桥梁配置") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index 488c451aa2..5697007b3c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.rule; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum; @@ -42,7 +43,7 @@ public class IotDataBridgeDO extends BaseDO { /** * 桥梁状态 * - * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + * 枚举 {@link CommonStatusEnum} */ private Integer status; /** From acd32f7b4eef7ad0a9ae711f85a03c27ed763501 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 14 Mar 2025 17:41:12 +0800 Subject: [PATCH 275/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT:=20=E6=95=B0=E6=8D=AE=E6=A1=A5=E6=A2=81?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java index 1609aec34a..3035791162 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataBridgeMapper.java @@ -18,10 +18,7 @@ public interface IotDataBridgeMapper extends BaseMapperX { default PageResult selectPage(IotDataBridgePageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX() .likeIfPresent(IotDataBridgeDO::getName, reqVO.getName()) - .likeIfPresent(IotDataBridgeDO::getDescription, reqVO.getDescription()) .eqIfPresent(IotDataBridgeDO::getStatus, reqVO.getStatus()) - .eqIfPresent(IotDataBridgeDO::getDirection, reqVO.getDirection()) - .eqIfPresent(IotDataBridgeDO::getType, reqVO.getType()) .betweenIfPresent(IotDataBridgeDO::getCreateTime, reqVO.getCreateTime()) .orderByDesc(IotDataBridgeDO::getId)); } From e869aaee836cb96554e83cfd6a4b0088b76aa125 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 14 Mar 2025 19:29:36 +0800 Subject: [PATCH 276/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E5=A2=9E=E5=8A=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 34 ++++---- .../admin/model/AiToolController.java | 73 ++++++++++++++++ .../admin/model/vo/tool/AiToolPageReqVO.java | 36 ++++++++ .../admin/model/vo/tool/AiToolRespVO.java | 27 ++++++ .../admin/model/vo/tool/AiToolSaveReqVO.java | 27 ++++++ .../ai/dal/dataobject/model/AiToolDO.java | 50 +++++++++++ .../ai/dal/mysql/model/AiToolMapper.java | 27 ++++++ .../ai/service/model/AiToolService.java | 54 ++++++++++++ .../ai/service/model/AiToolServiceImpl.java | 86 +++++++++++++++++++ .../tool}/DirectoryListToolFunction.java | 2 +- .../tool}/WeatherQueryToolFunction.java | 2 +- .../module/ai/service/tool/package-info.java | 1 - 12 files changed, 401 insertions(+), 18 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/{tool/function => model/tool}/DirectoryListToolFunction.java (97%) rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/{tool/function => model/tool}/WeatherQueryToolFunction.java (98%) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java index 9530d5bf57..8b235f9ac5 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java @@ -32,29 +32,33 @@ public interface ErrorCodeConstants { ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!"); // ========== API 绘画 1-040-005-000 ========== - ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!"); - ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}"); - ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); + ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "图片不存在!"); + ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 提交失败!原因:{}"); + ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 按钮 customId 不存在! {}"); // ========== API 音乐 1-040-006-000 ========== - ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!"); + ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "音乐不存在!"); - // ========== API 写作 1-022-007-000 ========== - ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!"); - ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!"); + // ========== API 写作 1-040-007-000 ========== + ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "作文不存在!"); + ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "写作生成异常!"); // ========== API 思维导图 1-040-008-000 ========== ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!"); - // ========== API 知识库 1-022-008-000 ========== - ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!"); + // ========== API 知识库 1-040-009-000 ========== + ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "知识库不存在!"); - ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_101, "文档不存在!"); - ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_022_008_102, "文档内容为空!"); - ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_022_008_102, "文件下载失败!"); - ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_022_008_102, "文档加载失败!"); + ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "文档不存在!"); + ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "文档内容为空!"); + ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "文件下载失败!"); + ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "文档加载失败!"); - ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_202, "段落不存在!"); - ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_022_008_203, "内容 Token 数为 {},超过最大限制 {}"); + ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "段落不存在!"); + ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "内容 Token 数为 {},超过最大限制 {}"); + + // ========== AI 工具 1-040-010-000 ========== + ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在"); + ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean"); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java new file mode 100644 index 0000000000..4e532642e7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; +import cn.iocoder.yudao.module.ai.service.model.AiToolService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + + +@Tag(name = "管理后台 - AI 工具") +@RestController +@RequestMapping("/ai/tool") +@Validated +public class AiToolController { + + @Resource + private AiToolService toolService; + + @PostMapping("/create") + @Operation(summary = "创建AI 工具") + @PreAuthorize("@ss.hasPermission('ai:tool:create')") + public CommonResult createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) { + return success(toolService.createTool(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新AI 工具") + @PreAuthorize("@ss.hasPermission('ai:tool:update')") + public CommonResult updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) { + toolService.updateTool(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除AI 工具") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:tool:delete')") + public CommonResult deleteTool(@RequestParam("id") Long id) { + toolService.deleteTool(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得AI 工具") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:tool:query')") + public CommonResult getTool(@RequestParam("id") Long id) { + AiToolDO tool = toolService.getTool(id); + return success(BeanUtils.toBean(tool, AiToolRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得AI 工具分页") + @PreAuthorize("@ss.hasPermission('ai:tool:query')") + public CommonResult> getToolPage(@Valid AiToolPageReqVO pageReqVO) { + PageResult pageResult = toolService.getToolPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiToolRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java new file mode 100644 index 0000000000..f3e01ee60c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - AI 工具分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiToolPageReqVO extends PageParam { + + @Schema(description = "工具名称", example = "王五") + private String name; + + @Schema(description = "工具描述", example = "你猜") + private String description; + + @Schema(description = "状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java new file mode 100644 index 0000000000..6d5a02e687 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - AI 工具 Response VO") +@Data +public class AiToolRespVO { + + @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661") + private Long id; + + @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + @Schema(description = "工具描述", example = "你猜") + private String description; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java new file mode 100644 index 0000000000..c85cfc33e7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - AI 工具新增/修改 Request VO") +@Data +public class AiToolSaveReqVO { + + @Schema(description = "工具编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661") + private Long id; + + @Schema(description = "工具名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "工具名称不能为空") + private String name; + + @Schema(description = "工具描述", example = "你猜") + private String description; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java new file mode 100644 index 0000000000..41e5bcc06b --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.model; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.ai.service.model.tool.DirectoryListToolFunction; +import cn.iocoder.yudao.module.ai.service.model.tool.WeatherQueryToolFunction; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * AI 工具 DO + * + * @author 芋道源码 + */ +@TableName("ai_tool") +@KeySequence("ai_tool_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiToolDO extends BaseDO { + + /** + * 工具编号 + */ + @TableId + private Long id; + /** + * 工具名称 + * + * 对应 Bean 的名字,例如说: + * 1. {@link DirectoryListToolFunction} 的 Bean 名字是 directory_list + * 2. {@link WeatherQueryToolFunction} 的 Bean 名字是 weather_query + */ + private String name; + /** + * 工具描述 + */ + private String description; + /** + * 状态 + * + * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java new file mode 100644 index 0000000000..3b7a0f07ab --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI 工具 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiToolMapper extends BaseMapperX { + + default PageResult selectPage(AiToolPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiToolDO::getName, reqVO.getName()) + .eqIfPresent(AiToolDO::getDescription, reqVO.getDescription()) + .eqIfPresent(AiToolDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(AiToolDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java new file mode 100644 index 0000000000..feac364644 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; +import jakarta.validation.Valid; + +/** + * AI 工具 Service 接口 + * + * @author 芋道源码 + */ +public interface AiToolService { + + /** + * 创建AI 工具 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTool(@Valid AiToolSaveReqVO createReqVO); + + /** + * 更新AI 工具 + * + * @param updateReqVO 更新信息 + */ + void updateTool(@Valid AiToolSaveReqVO updateReqVO); + + /** + * 删除AI 工具 + * + * @param id 编号 + */ + void deleteTool(Long id); + + /** + * 获得AI 工具 + * + * @param id 编号 + * @return AI 工具 + */ + AiToolDO getTool(Long id); + + /** + * 获得AI 工具分页 + * + * @param pageReqVO 分页查询 + * @return AI 工具分页 + */ + PageResult getToolPage(AiToolPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java new file mode 100644 index 0000000000..1ab17477f9 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS; + +/** + * AI 工具 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiToolServiceImpl implements AiToolService { + + @Resource + private AiToolMapper toolMapper; + + @Override + public Long createTool(AiToolSaveReqVO createReqVO) { + // 校验名称是否存在 + validateToolNameExists(createReqVO.getName()); + + // 插入 + AiToolDO tool = BeanUtils.toBean(createReqVO, AiToolDO.class); + toolMapper.insert(tool); + return tool.getId(); + } + + @Override + public void updateTool(AiToolSaveReqVO updateReqVO) { + // 1.1 校验存在 + validateToolExists(updateReqVO.getId()); + // 1.2 校验名称是否存在 + validateToolNameExists(updateReqVO.getName()); + + // 2. 更新 + AiToolDO updateObj = BeanUtils.toBean(updateReqVO, AiToolDO.class); + toolMapper.updateById(updateObj); + } + + @Override + public void deleteTool(Long id) { + // 校验存在 + validateToolExists(id); + // 删除 + toolMapper.deleteById(id); + } + + private void validateToolExists(Long id) { + if (toolMapper.selectById(id) == null) { + throw exception(TOOL_NOT_EXISTS); + } + } + + private void validateToolNameExists(String name) { + try { + SpringUtil.getBean(name); + } catch (NoSuchBeanDefinitionException e) { + throw exception(TOOL_NAME_NOT_EXISTS, name); + } + } + + @Override + public AiToolDO getTool(Long id) { + return toolMapper.selectById(id); + } + + @Override + public PageResult getToolPage(AiToolPageReqVO pageReqVO) { + return toolMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/DirectoryListToolFunction.java similarity index 97% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/DirectoryListToolFunction.java index 0ec86724ec..787b2e7728 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/DirectoryListToolFunction.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.service.tool.function; +package cn.iocoder.yudao.module.ai.service.model.tool; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.io.FileUtil; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/WeatherQueryToolFunction.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/WeatherQueryToolFunction.java similarity index 98% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/WeatherQueryToolFunction.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/WeatherQueryToolFunction.java index cce6e6d43d..99262fafad 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/WeatherQueryToolFunction.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/WeatherQueryToolFunction.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.service.tool.function; +package cn.iocoder.yudao.module.ai.service.model.tool; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.RandomUtil; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java deleted file mode 100644 index ba65093ecc..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.tool; \ No newline at end of file From 1e8845ce6d229130ae4a804e578d52cf20f5fa15 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 14 Mar 2025 21:11:28 +0800 Subject: [PATCH 277/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E5=A2=9E=E5=8A=A0=20AI=20?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=EF=BC=8C=E4=B8=8E=20tool=20=E7=9A=84?= =?UTF-8?q?=E6=89=93=E9=80=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/model/AiToolController.java | 23 +++++--- .../model/vo/chatRole/AiChatRoleRespVO.java | 3 ++ .../vo/chatRole/AiChatRoleSaveMyReqVO.java | 3 ++ .../vo/chatRole/AiChatRoleSaveReqVO.java | 3 ++ .../ai/dal/dataobject/model/AiChatRoleDO.java | 7 +++ .../ai/dal/mysql/model/AiToolMapper.java | 8 +++ .../chat/AiChatMessageServiceImpl.java | 54 ++++++++++++------- .../service/model/AiChatRoleServiceImpl.java | 23 ++++++++ .../ai/service/model/AiToolService.java | 40 +++++++++++--- .../ai/service/model/AiToolServiceImpl.java | 16 +++++- 10 files changed, 147 insertions(+), 33 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java index 4e532642e7..e98f87e0b5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.controller.admin.model; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -17,8 +18,10 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import java.util.List; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - AI 工具") @RestController @@ -30,14 +33,14 @@ public class AiToolController { private AiToolService toolService; @PostMapping("/create") - @Operation(summary = "创建AI 工具") + @Operation(summary = "创建工具") @PreAuthorize("@ss.hasPermission('ai:tool:create')") public CommonResult createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) { return success(toolService.createTool(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新AI 工具") + @Operation(summary = "更新工具") @PreAuthorize("@ss.hasPermission('ai:tool:update')") public CommonResult updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) { toolService.updateTool(updateReqVO); @@ -45,7 +48,7 @@ public class AiToolController { } @DeleteMapping("/delete") - @Operation(summary = "删除AI 工具") + @Operation(summary = "删除工具") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('ai:tool:delete')") public CommonResult deleteTool(@RequestParam("id") Long id) { @@ -54,7 +57,7 @@ public class AiToolController { } @GetMapping("/get") - @Operation(summary = "获得AI 工具") + @Operation(summary = "获得工具") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('ai:tool:query')") public CommonResult getTool(@RequestParam("id") Long id) { @@ -63,11 +66,19 @@ public class AiToolController { } @GetMapping("/page") - @Operation(summary = "获得AI 工具分页") + @Operation(summary = "获得工具分页") @PreAuthorize("@ss.hasPermission('ai:tool:query')") public CommonResult> getToolPage(@Valid AiToolPageReqVO pageReqVO) { PageResult pageResult = toolService.getToolPage(pageReqVO); return success(BeanUtils.toBean(pageResult, AiToolRespVO.class)); } + @GetMapping("/simple-list") + @Operation(summary = "获得工具列表") + public CommonResult> getToolSimpleList() { + List list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, tool -> new AiToolRespVO() + .setId(tool.getId()).setName(tool.getName()))); + } + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java index 908e9dcce4..51e44ed760 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java @@ -49,6 +49,9 @@ public class AiChatRoleRespVO implements VO { @Schema(description = "引用的知识库编号列表", example = "1,2,3") private List knowledgeIds; + @Schema(description = "引用的工具编号列表", example = "1,2,3") + private List toolIds; + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Boolean publicStatus; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java index a104421f5d..009e8d8afb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java @@ -34,4 +34,7 @@ public class AiChatRoleSaveMyReqVO { @Schema(description = "引用的知识库编号列表", example = "1,2,3") private List knowledgeIds; + @Schema(description = "引用的工具编号列表", example = "1,2,3") + private List toolIds; + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java index 6851564e06..3c72cf9834 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java @@ -47,6 +47,9 @@ public class AiChatRoleSaveReqVO { @Schema(description = "引用的知识库编号列表", example = "1,2,3") private List knowledgeIds; + @Schema(description = "引用的工具编号列表", example = "1,2,3") + private List toolIds; + @Schema(description = "是否公开", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "是否公开不能为空") private Boolean publicStatus; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java index 81a679a2f3..4a65796a96 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java @@ -74,6 +74,13 @@ public class AiChatRoleDO extends BaseDO { */ @TableField(typeHandler = LongListTypeHandler.class) private List knowledgeIds; + /** + * 引用的工具编号列表 + * + * 关联 {@link AiToolDO#getId()} 字段 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List toolIds; /** * 是否公开 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java index 3b7a0f07ab..d5d296692a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + /** * AI 工具 Mapper * @@ -24,4 +26,10 @@ public interface AiToolMapper extends BaseMapperX { .orderByDesc(AiToolDO::getId)); } + default List selectListByStatus(Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(AiToolDO::getStatus, status) + .orderByDesc(AiToolDO::getId)); + } + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java index 877ba77453..f310ba69fd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import cn.iocoder.yudao.framework.ai.core.util.AiUtils; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; @@ -19,6 +18,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper; import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService; @@ -27,6 +27,7 @@ import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchR import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO; import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; import cn.iocoder.yudao.module.ai.service.model.AiModelService; +import cn.iocoder.yudao.module.ai.service.model.AiToolService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.messages.Message; @@ -50,6 +51,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST; @@ -82,6 +84,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { private AiKnowledgeSegmentService knowledgeSegmentService; @Resource private AiKnowledgeDocumentService knowledgeDocumentService; + @Resource + private AiToolService toolService; @Transactional(rollbackFor = Exception.class) public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) { @@ -118,11 +122,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { String newContent = chatResponse.getResult().getOutput().getText(); chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent)); // 3.4 响应结果 - List segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, + List segments = BeanUtils.toBean(knowledgeSegments, + AiChatMessageRespVO.KnowledgeSegment.class, segment -> { - AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId()); - segment.setDocumentName(document != null ? document.getName() : null); - }); + AiKnowledgeDocumentDO document = knowledgeDocumentService + .getKnowledgeDocument(segment.getDocumentId()); + segment.setDocumentName(document != null ? document.getName() : null); + }); return new AiChatMessageSendRespVO() .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class)) .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class) @@ -130,7 +136,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } @Override - public Flux> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) { + public Flux> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, + Long userId) { // 1.1 校验对话存在 AiChatConversationDO conversation = chatConversationService .validateChatConversationExists(sendReqVO.getConversationId()); @@ -143,7 +150,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { StreamingChatModel chatModel = modalService.getChatModel(model.getId()); // 2. 知识库找回 - List knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation); + List knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), + conversation); // 3. 插入 user 发送消息 AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model, @@ -167,7 +175,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { if (StrUtil.isEmpty(contentBuffer)) { segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> TenantUtils.executeIgnore(() -> { - AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId()); + AiKnowledgeDocumentDO document = knowledgeDocumentService + .getKnowledgeDocument(segment.getDocumentId()); segment.setDocumentName(document != null ? document.getName() : null); })); } @@ -192,7 +201,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private List recallKnowledgeSegment(String content, - AiChatConversationDO conversation) { + AiChatConversationDO conversation) { // 1. 查询聊天角色 if (conversation == null || conversation.getRoleId() == null) { return Collections.emptyList(); @@ -212,8 +221,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private Prompt buildPrompt(AiChatConversationDO conversation, List messages, - List knowledgeSegments, - AiModelDO model, AiChatMessageSendReqVO sendReqVO) { + List knowledgeSegments, + AiModelDO model, AiChatMessageSendReqVO sendReqVO) { List chatMessages = new ArrayList<>(); // 1.1 System Context 角色设定 if (StrUtil.isNotBlank(conversation.getSystemMessage())) { @@ -236,11 +245,18 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference))); } - // 2. 构建 ChatOptions 对象 + // 2.1 查询 tool 工具 + Set toolNames = null; + if (conversation.getRoleId() != null) { + AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId()); + if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) { + toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName); + } + } + // 2.2 构建 ChatOptions 对象 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), - conversation.getTemperature(), conversation.getMaxTokens(), - SetUtils.asSet("directory_list", "weather_query")); + conversation.getTemperature(), conversation.getMaxTokens(), toolNames); return new Prompt(chatMessages, chatOptions); } @@ -255,8 +271,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { * @return 消息上下文 */ private List filterContextMessages(List messages, - AiChatConversationDO conversation, - AiChatMessageSendReqVO sendReqVO) { + AiChatConversationDO conversation, + AiChatMessageSendReqVO sendReqVO) { if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) { return Collections.emptyList(); } @@ -285,9 +301,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { } private AiChatMessageDO createChatMessage(Long conversationId, Long replyId, - AiModelDO model, Long userId, Long roleId, - MessageType messageType, String content, Boolean useContext, - List knowledgeSegments) { + AiModelDO model, Long userId, Long roleId, + MessageType messageType, String content, Boolean useContext, + List knowledgeSegments) { AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId) .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId) .setType(messageType.getValue()).setContent(content).setUseContext(useContext) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java index f0e07f2856..b0005c3af5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java @@ -39,11 +39,15 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { @Resource private AiKnowledgeService knowledgeService; + @Resource + private AiToolService toolService; @Override public Long createChatRole(AiChatRoleSaveReqVO createReqVO) { // 校验文档 validateDocuments(createReqVO.getKnowledgeIds()); + // 校验工具 + validateTools(createReqVO.getToolIds()); // 保存角色 AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class); @@ -55,6 +59,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) { // 校验文档 validateDocuments(createReqVO.getKnowledgeIds()); + // 校验工具 + validateTools(createReqVO.getToolIds()); // 保存角色 AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId) @@ -69,6 +75,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { validateChatRoleExists(updateReqVO.getId()); // 校验文档 validateDocuments(updateReqVO.getKnowledgeIds()); + // 校验工具 + validateTools(updateReqVO.getToolIds()); // 更新角色 AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); @@ -84,6 +92,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { } // 校验文档 validateDocuments(updateReqVO.getKnowledgeIds()); + // 校验工具 + validateTools(updateReqVO.getToolIds()); // 更新 AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class); @@ -103,6 +113,19 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { knowledgeIds.forEach(knowledgeService::validateKnowledgeExists); } + /** + * 校验工具是否存在 + * + * @param toolIds 工具编号列表 + */ + private void validateTools(List toolIds) { + if (CollUtil.isEmpty(toolIds)) { + return; + } + // 遍历校验每个工具是否存在 + toolIds.forEach(toolService::validateToolExists); + } + @Override public void deleteChatRole(Long id) { // 校验存在 diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java index feac364644..fb23224a83 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java @@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO; import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; + /** * AI 工具 Service 接口 * @@ -14,7 +17,7 @@ import jakarta.validation.Valid; public interface AiToolService { /** - * 创建AI 工具 + * 创建工具 * * @param createReqVO 创建信息 * @return 编号 @@ -22,33 +25,56 @@ public interface AiToolService { Long createTool(@Valid AiToolSaveReqVO createReqVO); /** - * 更新AI 工具 + * 更新工具 * * @param updateReqVO 更新信息 */ void updateTool(@Valid AiToolSaveReqVO updateReqVO); /** - * 删除AI 工具 + * 删除工具 * * @param id 编号 */ void deleteTool(Long id); /** - * 获得AI 工具 + * 校验工具是否存在 * * @param id 编号 - * @return AI 工具 + */ + void validateToolExists(Long id); + + /** + * 获得工具 + * + * @param id 编号 + * @return 工具 */ AiToolDO getTool(Long id); /** - * 获得AI 工具分页 + * 获得工具列表 + * + * @param ids 编号列表 + * @return 工具列表 + */ + List getToolList(Collection ids); + + /** + * 获得工具分页 * * @param pageReqVO 分页查询 - * @return AI 工具分页 + * @return 工具分页 */ PageResult getToolPage(AiToolPageReqVO pageReqVO); + /** + * 获得工具列表 + * + * @param status 状态 + * @return 工具列表 + */ + List getToolListByStatus(Integer status); + } \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java index 1ab17477f9..59f8f74d1f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java @@ -12,6 +12,9 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.Collection; +import java.util.List; + import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS; @@ -59,7 +62,8 @@ public class AiToolServiceImpl implements AiToolService { toolMapper.deleteById(id); } - private void validateToolExists(Long id) { + @Override + public void validateToolExists(Long id) { if (toolMapper.selectById(id) == null) { throw exception(TOOL_NOT_EXISTS); } @@ -78,9 +82,19 @@ public class AiToolServiceImpl implements AiToolService { return toolMapper.selectById(id); } + @Override + public List getToolList(Collection ids) { + return toolMapper.selectBatchIds(ids); + } + @Override public PageResult getToolPage(AiToolPageReqVO pageReqVO) { return toolMapper.selectPage(pageReqVO); } + @Override + public List getToolListByStatus(Integer status) { + return toolMapper.selectListByStatus(status); + } + } \ No newline at end of file From 9dbadbbb5d2956d68b62060bf92fec00f200a58f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 14 Mar 2025 22:12:31 +0800 Subject: [PATCH 278/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=9B=B4=E6=96=B0=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E7=9A=84=20AI=20=E4=BB=8B=E7=BB=8D=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .image/common/ai-feature.png | Bin 21387 -> 25498 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.image/common/ai-feature.png b/.image/common/ai-feature.png index fadc1cc5d660ef1e3a03fcdb472cba4187ff33b3..552ed59b424610bf8438003a1c839e197bda7eed 100644 GIT binary patch literal 25498 zcmZ^~Wn5HU)bKqt0@5id%}93-!jMt}Bhm`eC8bi*ARWTcAq+8th=_o+Nb1mybT>#V z{T#0Ay6^k_^73JRbI#d&?Y&q0_gec5(fYdTcZu#1fk2?Uni^085C|6z0%5Nc;sAG& zJ}!rWKv-e=+Hlny;O}Jnd}~9$E%qDBy>$zn%bo3SU%$L?aZZSeeC_RZb#i=jerjU; zpRaG_fUD8-zXB8S1(EmU#Xw{wA8)YeR#MzKcnBg(Y|nTuznLE7dBkB za-RrhCU-2s!?-}UVRgS*?mv{*f}va<54XtHj#Z~N!M(B9ly%Y5;C>;C@!QGajZ zM3qNhUhBrl@$BjhdaAp-`^WY#j4E{RUCa+T*;N#3JJ4&}%l6m7%CLonc~^2fm=}#k z`wbRtR#y)V4fW~j?zVmZ<>$YYopUrfG3M$v8yzz>HFa@(b=vfGE-B+`O!E4m?{dQQ zrXpm%VSm}F`+WQCYCZRQ>DN};=*?)$O&~}+yS<0y;Q)qGQh1^+gqIzr}$MsC*GEm;HHzi5n;HT8VguznS zykIy@D-V$}ZY$mG2i3!J)>EazlPjrlBUkJ4M$_Rl_Q!T_ z=u0)?#Hv#~HD!(n2;bJ89G@34eyANOw$!`lDm8f`l9MBE`K?C)?f_PjuKt+*)bFSQ zjr#IaZ?paKv#p6}P_TQ^)c6PQ3HUY18fWRxn7=9d(U}mSEqXh4E>-pMc$xKxmXj&t zgeK*{p90aI0`!8A|Lc(~X=*jBjO8`%kYMinUUXXGh89tG;O4j;F3N)ubt5-7hTNHn zZKG+razgbldVE_GFGJd8R@#a`;S~0BU02SneLuPyE4x^Em_Y;579Plp&=N{e2urj^ zk)zq=gUVj`R1pw4AN7}OIA4lB7!+eXo?`IxuyeW;>t|&jW7~@S{uxcaiLHqw!g-D& z=i`tIs<5wXJyR(nz8osMri6-a677?_( z7KTSvQ{+PW@O0g;fm;t(DgJ%-u$%H$q$7uH5b52Vh0FT}xS?e)+(U0g63SXRkkGKZ zLe~dK-Tvt&;sS<_hEUVl&(njUTN?fOYal7X9F9W*m3716nFvN85U@_$q&2yHc$zR_|Nmawqc7g#n9W{!w3GXptYG z70l~wCY0AVQXB8Wf@SOy;z-HRky0Da!5KJRL5Vvp+VWQsZ-dB=uo z{lVbT*9|;wU(|sjphk?6wigx#CojXjexF;0{4MthG3P@kqaR34i||gDcbPT@bjxGU zFPSAVEa?K+h6H!6nE~svem@&w3NHIjm2K-`hCHe>E&?r$O|i31yVgE*rH!ovo&GBG z54HHC*Y#?E_7h;vb85B{d2G2RE>;ziY@%dkazmE+7Gzj~^6}W6{`}|BRUKxM_)Gs- zCPSs2cq&>$7%q5Mm!A6H!3^WgBZ$_?lG(|?z+Deq6?+QHW&*Mb_!T0OW3|VrU zVQYZ9L51N7Trsp&z~jQn1Pa4S&= z>IikK3&S@E{QnFix#^Xx>V#rF(cJ7^4ZH>K``ZXL?-EmnFS(HV#J18u(8v`^t? zc}+(lo0Kyvhc6FSNbQz)`m&|WP8EZoI;tIt^h@5Z-=9>O{B>wD9>{25?I0JJmyc0j zvD!Fm`MNaA9HdmhZe4&CDgAgBB!2fPW`qrYP4iIFw=#+?>&QWhJ@Fmk49&>hiNQ;X z&-9VvrCSpE-lR{7I27q|_${K;<~j*ALi_AKw^WipB|p^ z!H4;J&7}s8UQ%QI$jzCz z{yre&fz=PR;u3r&Nm;c+4Tl81mA0fIBpw9iw-$sGZTb5Zl(1VlGZx|YQ=x$l*HOkD#;DYsd*}bi z=yfO~YSz|<#`Wcwe2^ZY>SLvdeWnH*(^b_2)W#pmpz7!YHK9fK+Q>(BQ@5Z;SiJ!3xg=4uCu*VNz?@uv--=tq|fLCQz!fIki~Z5g&t z48PV)jX(V?WueFO`io0dZx!ej`rNYLGgTh-d4Se*apLMA)$2X=-hX*d+Bi>t;@Iu{7Cf@w z#o80snzyxTxr5bZ&Ii+;#oe8_f{NpRKW;_02Kj zWJ~2@nH+Eo$Je8BQ*Ltk)sWQL=&pSDVm1#UZDpl~-@hTKD8pyYQ3TH9CJc@}BE>MK)4h_?N$c{N; z9qco$_eH^1GY#ACDrD<3NZ(*Z#LNU-dq37xf8mk`x%ntCu}~a09jZw^c^+x zJ8fA&mv4tA&jwp4LSkOG{hq1sJdA-4X@3@&n3oNEZcQvEqB^rKpUoiASg&F2!6a7p z3mP^c44qBF;Nu@BP^aLZNUEU52|j|oo?lrsu->( z5QRPQ(suqOhpdjes&jD|3|;n?S$9JWHYL z`IBELEI#qD6vaQ2CE-%B_~VdLk~qr6TAr0l(;CfLUpV3oua=apyVidkpYC()?jJV) zm(rL4<#8hdM&D__zN<15M2(EttAMax{r&l9UhyoPlxdp}Ows43=nJ*hLYAI%8|uM4 z-kBy!O+=kX%Xq=5XkOU8vka;xZJ+<6ufBSEu;(V%CDs<=St%6jKD1DNIeRbrC-Uyp z=33F=@*hax(HQZ3K;ZseX5nQ%Fw{o9qsTFK9{G+@@y}7|iM^{^Pa>uj$^ksYCHEZ+Urk=GL6uCf-h)c1LmX*L7lixGOcvOhq zpIt)^6URq6y%C{-dPl`P$R5~2Ipl_Wk=(R`+U~vHeiTiWHg-x1>bs*JGnsKd+)Ffy zPZ}KG@p(p_gT|o%nS$w_|8|&MHSyJTdY=q-M<tZfpP1Oq#D-)rel|r>`JO@#551lpu}ALRgib zW!BYVp4U4{^xi1L70-`f!W?nGf9VRZ{&SobW22p^LOqR>=u}Y6GgF#{^wC|JaV49u}E4sXFW5R;)n%({{GDwrqa!4o!74s;P-y`#S# zWuvGa%Jp=>@W&H%vrE4L;b1^Gt4R>at7kZp_pA!cFTdB7ZM5|yS_b{sZD*$yUt7G6 zOY8D`Q$vYd8`q}hYDPlLE10zX8*eY#$BhRNpZA5xVO1c~8UJRlJ+9$2Py4s`i?#Jn zLFYTgXH;!ZJuDtSH=iS5evd4?x(PTN8MygdqUQl&ae~za?2j}V-#>i%Nd}V@7eBLMV5=oIhZkDbmBdoy8gEmWP32J_k`xht` zmP0X|FYAhTpuJCO5W5O=l|b$`5O;Iqouk1~Mhc!rYh1 znq2PA^)3~P;ow|plJt-6@}QurMecjfBN#CmdXjKu z$B%5psTmE@o@N4{LRN7E)jmPGfzUe={Cq7=WLS)niFUs@bC+h~C1HA|&nEd1Tb-q> zQAWwjJnml$+FQ67z&xBjVy&QqAqN7Gm#|0)Wc>pD_k2cc^?P5)%pEquu)AIB? z5s*xh3@+RcZZpT4jFJGYzNambD3ZC(dtG~7cfT3`DY>uFaB9R&WR%(~8Wp_gNUE7b~U&v1Vo#>y>^P6u(8)$!omppwz7T4kzpv zi1i|sq$i;)BHC>_((Lr!q?wuF*dpGe#0!wjR{N5rx#IDJi*tq>?C}O^9F_otg}sHf zf#z;IFXi-`@V0zYLFGI>pZ2wQO7)5l+ssa1Cw%Oh7XvdqRM6x~=0#KwXguzLRvUUv zOsKg|`P(IR3Xq4`tQMWg62yH`*t)%G23b0AsIq0Ayjo|B@-imD*&FaQxn)rppCMEE zxN&_;qnd!lVmScUH(x$s6c%6W_Qtlstn#dBXqA47%z)}~qVBhPK5PC8+vhU%o!Cy? zG?1i;BuY&l;9aVvfn9ae^QJeUtz_Y`ara-xQw+7$5&3YhTBq6DyjwsHIV|oWmrm8d za%Tg2FX7c*fcdNc=Ven5@e5JCCrPN?UEv zoPLhj(*?6OANM@b%Tq6Yk%--6+*=;U&GEtk_Dn4Cg#KXP|_*#w6XtX;uWIPT}WiH~I&5Q&hp>qfn&=X(p&NEC0Od=x2D3?KBvpp2HGIASXMX#x7Mq$4eaV=Y;$zVU4IGG9Pra>^za*^8U(M`hCYe%M*u5&Tz3$rQzgpQFgBc zpSRHA1uWj>f2sO~8yxFyqfbR66B z5uLiTe{9c6W5zdD$^kIM>g6VWV&Kr1g{nl0m&cKACkiQ?3xG?0 zUtrzZL)TvlR$sB#HJ!3emJ%FQ{{lQjc3F*bwXt$#yS&dR`c~(1^7INW(q!~{B7Z{h z>SsWVxu0MByOto;6|z!}9xl(6aG0x&2~KyNwQdEkaBkP*`OneNwTpI6LQ&NsKs`0D ztFB#Mdo@59Xc(|tG^F@upj!R)-o|Nit-{B)mWAW5TUV-~kC=1d*LxGkDsn~hK4OKu z^%y2@x{yp}dMHQ1sQ3n>Q4s)eJU|x=PiS`c)w!+(Zt79E0n!rZqcA{A)9SW?L;2`8 z_S*xRu(deU2Y@?YEpMs*_Z*Avh&2P=0HkmJ+nNz&XS+UeQbou^d!UKeoDN*S@6(Gt z50RUQlhWI)4Cqy9CRh-32DHCGH2a^qj>arWR~)5G5yRm##CWvwaad2<$bNp3HCS;e z1RL3yY*|;KMz8N${Q2~|GZC$+ESLGpdL(Zk?m${PYb;=$$z|YjKH$5i?Pp_mxHNbt zXq{MXRZZRYjb3`&wjPywM9o(3`Io?%h0rHXz6OpLFPv04NVf;)l8c`Q?NwSC!5=l6M@X#&xd2i!P& z4~m!A$gETAeZT2i9JukJzZ~LYE<`BheP15^?+tA*TsHZgt zpCCF+G~;Dr+hhW?a^`!5M0((VZpfuz*3f~w_78u*MGhw*+orsE!HwN-?+o=RSm6HG zNp5@K{9rM};ZVu`Ts+|v^RDxv_#FDag)HP);X>@GTz)14|3oN8W>=hwa9G%Wjq3ZQ{ znB7M8nO5IV@_N(7&9DVj<&&4ZV6w!N>;?g<^AQV*cli{*yloQM(X6Eq;Z$f= z7VLoeA#_t|=O@K1gW~Mgk?+G79b6p zmvHtcP;8}lJgARlMW7h2XLk$U#A#8G5`+~V+Z z??sK&@LU$F6!tJQ|LRy86U}KEV90BaMl46NY%*_mDbIjMQ%1HU$1gpzH2akOSLxTfxs&zn`Ia|(>nSfhmL^$FVOVzDU@}5@6aw`Q^TV&9?sfHo%{zD(2oRyrq>>{}Nz{jH?)y5EPh zcj%*zx*C&je@1v#AHTC?cKu08S0-}%P^RLA!P)M_eBk+FW_}Cgd^5n{Csqbml3ArW zNR`yzq+V2fRu-(_NMEMC(=I{NP0+>?E=q!=!zN76{K}N4TI^}@H6ezs?D3Y3<2_IK zMFU^&d0EjqO-qxegOX}5W*>7l*~M|Ue|yMIO&nf576m^Ks#%; z^4<&fJmW%|>_K0`Q&}Sy4~KA-9*634V6{Q;pkBHDk!W3H_GH~$R>kH_nec~xExCNOb+R6y9 zA)8bOESEqOq?LK~J75S^PRIJ{J@HxMCrElX%!$IvfMixlInzP^B~&$lTuF-<%vIl-Cv5Sg zG#c4v`qJU*dtd0Q_dN@m#s6L#ppl0ypZL7srnhhvRE?mG?YSb0_>ZlmA0gF~Ah`yT zJorC&PUl9HGH*oBQK=W-1y;8ShtqD8Rb%}xBp+=L*eTqfMa2G(d`E-&U%GQ=S_1#u zVSHO)F;XyeBL5%07oXTSTT%&Zu>1VGMOb9PYp0oq&hz>^p36$j&0i}pAtV82ml(o) zeJ1sf3Ne$U%RZM#$%Zd((dCK}w9K>T^^BA!9K+|=i;PfcezDp10DLskVIQ}eP3BHW zuPpgr-YjgdM_dlcz57z~A^dMO4thmbP?gSRLEL1-PJ56Ndt(!ab>I6M^ab_|=fsxz zPAy=&680+d;c`ojtZA=Jody{+Ja6nsJ_Nr#f|f2z^!PqO9A^c>9*Z~NnJpfcrATjx zZNfLJI^ic7MDkP^8TAN@Uqzz76e)6pF4xD2`^4k8VBjdV<>T=|$gqfGLD{tEH9d_C zTVL``ED99uw@Y>roy_#R81}_5=PzFH!{{fgwAFgCm5Wab8a}Dp(vJ>Ti=b>U zX`g5EBR6R=#KDMau?Ztl3T|PE)MF3XC&a{Nq&OqB_NAy5#C7H`H9|tiC*zb+E(t`9 zRVV9#Z*bWCCPQ>Z2Yz&y@iHyaeUb0k>1DBzaXfiX*?I7nnr5;1e8X%uAKntU_lH$C z3x8SyKmqDzz7v5Z)sERNYvY!ACbb)9S-7@d}9Xd zrzY_5f-Va{mFK&6C*Vw2%x~+HK)qYGaO(+^dWjMFTq3x86sC!8bGjQfiJj#ig~4DS zM}I-pQD!~C>hD3pS;!C0+bcW`3QB*bWtlr0P3wW}U?wGwrhL1;uQszz&J9y-P1RTX z`%ylljZd@z`o0*+0Tf>O@biqs@#yPCoA zsgP=^=e@)XJ_U54E%@^{2p-3eeJOwY4T79WJSdJ#RS3C7duR@arA{xe`TnRV5vA$b0@*|I(Eoh@M!eYR;g+k zsd#rGHtJCHuE*Wnu7VH-dp}((+!U1>_M0ZoD`5G zn5DY<_{ATCwUg3#WSi_}?e#*W-}e+PMqtcgXM=js{G&`{8w=uX0!HV>?T+$KtD5U_ z#lb$VlMo)nKNFuQ@>D4J8J-Z0^rXL?EI+u6H~qmD&L@Z~z;Vq_C-$B?$1$f(3@%Lt zYP2JH0XjEv<0%;VF4xE54nrFHkHlPhxxy1!F}$(vpj!dX24|J*4RuAV%74n&w9$)Z zaK;s1+Ow9&=b=ny2QHlj7lWc;JAB*B=g(;uBo{d!jeOP~IldoL$lI)}nUyZ6{`b3S zky|JZP@@R<6?)cBX+Mx#WEkt^ONJOmZs}>Tv({-^5?T?J#Vec>->ZN`5O>l}CQ(96 z1XpLY*1hf!&hx*SmlB1j9Tt*5WD6(68ZnSpQD8z50h1eXy!wt;^Cw?8%Bh`i=-JeM za}xCPtFim{=%1UCMa_-ED4sWv3Gg8Ps@Bv`=&oGc(H8)PcTz`1us-kkD&Hd-nPd>7 zgc*!44wvD7)Wh53d1n%FK7`-mm-P3}t=63k977NH{DQ7TocmL2+2xA3!QTg&r@GvQ zU*V_vKnp&X_z#JI>C;+GNaqCyny5LbhAY)npt^a}BEcA8khGEn)L|#14Keu<@)B+% z$VU5@0T4SV0zTftdBbx!dp?pe6?Sei{j!@8igVF-+&NaCJ2;!We|~hleKf83<`2{S z9fILU>vPz~rZ3@YwA5k*ksQ3kY2OElv3p3Z`D@pHJlIj~tlDum>1`$?0CF3G=5baU zgrwEpIIFFK7e`@Vk*6DDhR=h+idRJj9=nM;5~ZSD1PPs z7Rnfpx?6-~8rr8;&9#_$_X#LsyP>v}B`x@Fh9M+BQ#u&g*5DwCZo;dHi)%jN@jMdQ zEfnP}6z1SMovXV)JE5t%`g7g>wOp~w+FI4#ouny9;nF#n{Xh~d>d=cN=eUBMbaWOV zZb(Ksj2~L6`(&WKsBu^~V#k!F2_ ze0VfPH$6&N0%jEBF*j79w})^eLc~7gAI97)?hK^MJ-xIu1JNctCpeM?ANG1Nccksg z-f_yS_b#9pd4_}${g|pstr;5j4(pK6#^nnHf}2@98}9aIKYMi|MI&&nle2>Nj?09w z`B!art$qcSH~H}bX8NuR4bBaTDKQw zWfemU`btxO&2G{nH08jdsWs$xT$^PeiOV8<%lZnOI9Umf4+`Yuew~zZfd3>Z(dq}> zp>`;uI4#Z&!dEQry^gcnCYvuvn%Doql1`8h906_okOmU~IV9a|VJPudchJ}CoO=Ms z9IqDTsDWx``EW1=h!}t$nKv@vrt6(sbTxd0&;?8b`>ww>OQ3Ls9)o-YU$n)0!26emI+&m`x zPgtX7#9U~?&bJs0lbnc~=E^RPXM%5>3IN!!NZ79%9asK-^sASu&PO@57%VPp?HYHb zn@GEDJzgj{#PNuWvWEO_X_lU|$TLpQDPxctUTaq2+Bd=ielaByk*r1G1dP<_UU7}b z1Cr&xO=Q*U8>xl~f{5zD&MI3vet)HLlwkNz?(qIXYC91NYCFaD@{7#}Kck=f^Y7b~ zd`2`uD#KL9gXVbpBTndNF6(_CnORpCc3E>quRZbZzRSm4KT4=h`-Uv65gvCO7yZ7- zew+P3UfoZuIy^$yIYYF}hRHOo*y{n_&RbMRLoJ^6pc)sQu-6c;M-;N{oMg|SK&qO{ z#a400X2X{*0xFQaQxZ1#8gdx)5T;}=DoCOB`MZxm6cSylHR z*g>@XYbr!B)zfWMJm$oBl24!-G5p*@7an+iHq!l+^i9(d*@ z^$`uqo7@FytvoZL#My|_wz%6mkir@54rn*wo*kTpP}NPbc2g1joc+NpG{PuzpSOlr zavWL=H9Ep8tzS4h+8kLQS<^FxgGS4)Dye8vMURJ)?z87wGt$iVvkD9i!o&|6P|!h; z&voS|iMq2dtcYS{#}Epd!1NWCX$A!sf6nwj_M!7cU+$l+{6<%g;!ehD*|cFuL6a2W z#kEH~!w#I;s8rEUji_f-G|Rqui9$foBo;gcS9_+ZBbD=XT;b*dYqAzuhf{x#9nvWg zPv&W92C{o=h+A=gTp4%2hGiPd!LSx8a9of}5mv^FkASqQw-rfARH#~imLd2vKI&2= zI;o2bCeMBE0_tXzwmit?#8O(fa^|(w2uUFqfX#ms9L%hk70pbm5!ToN6;?PiV}I6V zDYV|I{GrNZA_%PQ%XTJYpzQ%Jsyy1R zhv!8+p$iTwNNScIkO7BfLE774R`Nl*ZLmd@a0_r)S$b<&CIr>N-!Re2d$0)(0H_8vHZ( zUxP)IIWB-eU)bW=m)P5JE@k?}>fy>e4~!-$Xf$!zjA!41X zSF@FUTN*zb^)C#3uZtuly81&eS3fdR4I#*%K)*hDF2!D0?6uAR?GYUR6*@znAZfBT zkrO}W9*UT`(hEdlaCpAw7S2Tl_nyk5o7D(c-xl1NLcvYXxE6Se*Q7gORtosi zL6+aJW|KoryGmhDZ|5JA22RdL`BOa9i3YD_WAa9d56?!{|9oTix!KPBBL};a>r~UG z0;lYLG=<{4_a|;F*_o|8#g9F^R5ln4iWdZ@WRmS)T0ON7PRFPi@)`n!Ccf7RRemQ> z$lEXlNS}-fwY6hZBB$ zE0I~RO6zJtk-e{)xGeZwr-v-yTI+sCq=QHGaK&a>8E|EaAUHyDIP1Hpni_qlM%<8~ zDYueym749iQYP^8UX?MWcQ07vgC-{!=ZX&_PtAo;5;19yoGb)3E*djX@>@Jk9MxqG z7Y)kYlg!O(GV1Ssk^~!bAkypuq*A;Rh~&QUL}A{3_3q^@ycvt=&Cf&0C&cQ)kQBre z&p0Z!iE5@>PE|mB2|xrc<4Xn21-^fN9D4mkr3H=;L}&jibQ6w(NCJie2ts=wn6HZc z0n_?`DzKAy_2p-O+u;8%DkFi0*I{||>Z|lX0E^Y3F3&>xL)Qmx!&##GRuT5!{|#=5 z>PM?D{G6B&x2`;}PX0P=W!L80*}!w;w(xCe@1oRn5{_g z84P{IU^lDxbefzE_YsL0xu92IXa|Gc)r8ElT*is0i-AeV9E19*39!=cYSheN-v0R) zpkni5sJ%7^E{dvdFES<2&s2ZMArIw&g*`WhkMmUXG+ny@iI%0^N63X-Nm~l$wyd)q zFFL&LhAVo(N0*)B3CJe29y-edP|UM6hU<=sse!${j6rrCdWIL}#mi%ee#)_5{ffglVtAbXBgR4;=8>8q zv@_28=u3w|h|ub@!H*F1@92BQkEtWN3;}z#qcrh8OiwsBEJOdm3ffK5Sm#u^4)mhe zIEYq%{o*b9y+2fVIQ{$S_0=*u==Wt%w@g6>LIh<4zYzUhvb!(?oJPqj6~G z+YFUX+i14fn2yleVDX(ob{sM#8LbicA|J3PrCGr?`&$;VtF6d>BMlm!doUt2kJe_c zvtw*Ee_ig>D9)W9t@cc$&l=Q*%0d|tt=MeN(zU5oIWnL$#d~4Ok)3S)v9GT9FrMO= zj>_1W@nv5+1k@kob7eJ;d4EzWckxQJ*uQNe8}-+|=%{^?Cnj5y3&MRbPNRQQ?oFlU zYWgQ3`BH-^%WyXX59)=nN-gns^50$U-y5PSMXGIR`20<#j56a@>1eDXJm_d<)H;dV zlYU=Uri(cmI1aQdMzJJcOViW3!pnunInp$`^b@n^VFmhRu*`0c-fc2}zAuHt zipTb{#oeTlNGt6xIKxMu`?*tl-|kLxKCmMYEA%nPr8;L}b-waFi=I*f8IK0D(4sX$ zs(G>TqK4|)xWQ*Dso`%|Avb{KeXZY-9w2 z9|_@g)s9+zZXMJLnJi;#6DC=Z!RdrqUHs`v!4QShG?xxA(I_dj=>G){%1`CL6na*F zyD4>lBD^9eEy43e(7J%DFnIr6{E5l7Dqj%CdO$5~`Rz=(js>fR48+O`M&0nOlf z*ci);>;L$~AdW77Vm#EiggOR@uBfKEYLh4x?VcO&!5|eZO$^(5=Pa zT#w5RNPu-(4Hi|k;q4Og&wovHRDY+pksyYg5S0Ln;s}#rkq}q6VNY&ptr@=#cxG3S zxi7iqr)^Zc$oo5ZjQ4Dc@->_s)zZBDa0{B(sg>6{W*BN}Sa~uI)^IVk!O{ z`m*~tW37U>w(Bnn9ou8JZ7edg*QK2*@{&1L3oCdco<7Sn&F)FMQKKDC#1zs%-*;cW zqn;$aDxuw+$o8#@ePjY6Izc(PRJVAh)4G7oR)N<_$ck(CcbC;#qUh z66^W)t=ZuU=Ie@MwELh#W)ErbLpp&onXj;6G%3|BO2z1R~(Fzoy{VS=zS4A@EO zsS!P!cod6-#|wk`dK-iBKYF69{UpKpPFH8E6LfiopL6e_a^Xq?Hk+|_1EUPPbr3e$|#iV&J65^TTh(0oL{D3($iT29~==G zudS-UpPfk%&3m!SuXVMypHoTXe#Q^PTSamJXT@fjV(9Xcbf|GiZ7z$!y1O!DraKn% znt5ab1P>+nqCdsLqFyYgAV*(POM|U}U+}bmKs`cX;5}+4N6#@xkN1*oTTc&esj1Fx z01C&4N5@mgI+TET?^_=O>|j|dhp0cb{=z543_P5=gb+XKf=s@~9x|4Afzi#&aORh_RpCxE9A|3Bl?(lilP880*| zzs5^eHA~fU1w`#i6Rxvy={P_~XW)-NpvpS%nw3Jofu0Z^Y=72?)Sar!(VA`bD~b~x zIrg;t_$kxz#{Rk!H8b1iE~oO;=b8yXg)Wa+klT2A-TmVTA%qGLSerJ2h}EnO{7=!y z!`nFbl!ckhKYtZFw|^-EWcazdw%6u@6V2B?uUg=w06t0$>*01%UyYX}Jm9zKip6=n z*0CH&7FXZZeP;JCyGQQ$?fSrdK+@>;$k4m)$swXEfJh{xYfw1}t3^_VbJOlu6oyR;~8IFvca`sgI8FEI{oCOlI~x2SOUG z4APGpqx~GdT_nAhJ-hQNlI5V-6F4-GJ@H3}8-Ud@(V=|DaG{0B+RP<%?Cfe!Icoxj z5!e}osAxbDKP2_(-3+_$E~=S5=rEM(>5(|^I9dmagO}Su*S&buPKepFJ+fUvzrf#b z|J4wB{t*{q*rS-AI(U#+4ktjaG3g7bPH zY#$I2@oJkZfr7HeJ3MIC(E^P4OwdIrtIJjWB>b4oxV&7SdacPF8?(da(Jq}D8ctDM zQ0M|FkQd@N>wO{uyjOp@ab%#$ng^x0w~4Rb`>K&4Zwci+kl%sS1^XVBKjuQJ<`nta z5y9@koh3svu_Y8KTdEJS$EXwfK)0`ua z5ifSwysna~0$`JKWzF!7ruW+Vg*7aiA8Yi;fM|Sr{EO^q!09i&#EE|(HK@Mr$!@A`UT8Z@MOKpyHQeBk zD&|JYyvoN^H1{$K?6uX}YFt#>!};e~$6Q{+9|1(85r>@}JvIELJvgP->K)pLpF7@| z4T*}O&R~JsRs)WGzNvrpMq;w7s(-XE!M0{x8f zrGNyTdcqT2S?pt6UhWKgS!$mDib)TOjpf!(cNC6H90VgCm%T>e;~Ezt{9WP;;)z02Yug!3Va7 zZN!c0$KT`f10NA1!`eiRmI`UVG`f@Xdh^s)J*67EA#JcTIqa?5w~yyp5WA=}?>DaY zCUy+17%ROb#b~0viHV7g5p-XE3c%4Dc`zokkakMV$dRf034RNd3Ss_(>Fu~&gL?jT ze!7+vK-BT}OUFAYMkoeX*BbFWhI>(WlG1+|vL>4UZZUbf#JYrPvI0U(PY4lBB&AEL z)IDyem@jw2k!30|P^ROtkv+S+L#qHB&H=D&IHo~N4k_)O^%*)B*1!z5$vt#BCFE-x zzbp84{731Y%gnSqC4oOHG}gq~qjNS!(kG00^Q{~`{+GzBc9`!n5L&Dj;Ckl58nkCe zT9O~J`LDq9XR#`;dwPCJoVWjjRfXU2K@ddOZeQH^2H8{aTOjc@BRg9EogpAp+2Oo| zl79=3I~P-|yv^lizj1dU|CXt%Hie<93W7p|jQ_LgWI~MRh93L|NG=9Q82M=qmU{g~ zaUQ<*?`m>%;evZ^B8{p~{~G+eZP{I$Kb={8`G-t>mEpf_?pG(JF_mB2zqFUhMGekU zW>_G30?_}nF>cuDv?^HsUk@67p_Ye1ziEt z6zAv`$#VCKDRYM|pwNMscg<0}Lr`nME;!?FJnFx{dx2n$O38%OWm_y9psPNPDn+zX z``3r1VGfk!5||=DMP1{c1142M{-9+NWEN4Ak`m!5Nk2EB^c$DS0WUTE`PH&DQrE`Kcs-6Q(i8yEPnRSq54b(+er z)voe;$Yzk+we(z$)9?`=A&YwFA-GJSY0DUHP*}|dmiV}1OgK>G(nF*H>luY5d#~|_ zlZOEQsF$k`udwf#C#99AH$qv+#LCoqy%~erZpHS!fJM*6=Zs>L`^y~u%ima$no;fQAQDp z9EZpvW7r3GmuW|qter8GJI2E)z8i}3>yQ%iF4?=L)*Ntpckq$@t7hnP-}re)JrF9j zS;-CoR;|!>94(2)AhhDI$Z3peiU{MS4nKb1z-VD9K=%-_z86lPTGrmI?{Aj%FZ*Yi7x8*p(`RF5IO`96h?ZHBAw7d2n3KSEmQ&NARtA85IPt_6Ew6?rKl7^ z>0+oNy-1O&G(mcMqwllUUF&|hYu$Xx%$%9z%-OTgIsg6JXLyWd6wO!RI>y0dWW&P0 zfRS(>DVSXaiq3rMzOtK(ki1IbqWqPCZ(KOPd7Z|c;Rfa|u~h6BzW$~;U#Y-mEWy@s z>5cG@jSV*?g)`>nITpUKrzlpFMMD*1iSBubzYcBfgWP8xzCq)ibJhH-1_VqJ!y>J9z66{zfZgy=1k8?Mh; zyZE&=%+nq;P0H=*u&%Sl_e>NoAEi>QWW6ZXBK(n3V-)4=?7&XvTh=8HxKFRi{b?%r zO@00c_ z9`NtyK>_7FJfl!P!#I(gxqP8QV`q*2xNZ_RhJXxn^HZcnYL|DZHhd(uBU_6ERVct@ z&+gVwP|RQUowfPg?YJ?9i^F&CdB01>*Sze-S zoiP(U4LjcXkbF6FrtOmXJh$ju4wMfKbVI>SbVz~rk~;j%5;NYe4v?DE?IBwZeYQLp@Ca-Zk(Fw}(l zf4tju$_xgFAwzaM1I}5XJYqhh*e3WBJjW|MbY2Fpib&zDlG3~h7$eI4qH7A^@D{lj z?^m{~cwCao^B$cBv0Juehi_mFARb z!ksNo|G{K$m4>JURH&78-AF|2k1-6(eTv06AWX^z7+$O-X)-jq-6qUqSg(56EL5<*56Yl5#8|J0EV`Ulyw5mY5ItJiFy{|Ng#=_@n?W%&lEg5f9vRGQ^iI#2a7 z%m}&q5BnDr1fq9YU(W0PBTQMO!ac7X#g_P`!LG&;>njP7Oc6Rl3ugb)3Pq6D+`@hS zV%(#<4-SiQLG*tl&U|C~^FPw^6hEijR*f!1(vqd(M!&>{^T%qvbPv=XA|%cK0JG9=sLn7+LAc6ZwS?_`$9g89q?0+m-q;T;&t1NSxo`R*`8kwiD8*xct#C zOsVQu4Wf6`iyrH%t{SKFKql+O@b`<|HG-z^?l62NFh_Evb)?N7NN!uf8&HNj(=FC0 zGSHX18fSE;;zA3C74Tbys($5oXeh*yo;&fQ&6f6Sn%_fY(@-`iVODXT&;-Z3gO;y9 zrOQ|*at{$ClIj|%uatZ)O@1L z=y5asY8?>ksi`5k5@>PG=Lk^+T%q9IljKObO=6~9%6<>~H!zW?z4yGzP+)FW zoos;IEr<=>V@(n-2ec4@1fFW}QN##8&a(2H>DS=Jw6_nn5hV!%A`iok&rUuL-pk*@ z+V4LJ8{X?zq_5Ezhz=!uH+e}26{z!-|jQ!y|MR(3M76uzqAlPy@j$rpL zbd1ek4HxbLYHY0yNhk(T=(aPQ{OI?Fzun>cVu`?P>S~99v*X?6UkiJ;-|y)K!;iMq zInWcM_1tl34MT6000!$3SR9ViP_hU0)o7TL9sC}149jZFXx%c>7LJ5*pykQtAI+0T z%*Nt}Yzoq7HUtQ>eGGoDOyIdjS3a$*jS0rFG(GmMPB>JZ{+&9~vp{?|EZ;u<7Qxgn z5@352W`DSV2Mu-Ba9i_pWLW`SQ~J|E;?VN^4~aPClQ1zN;_=T!NkTyQZrl`-u5lCT z8gB1i`z>?8!(BAvxjG+pRIG3}FX1kx{k0lK4QoI;Iz}1v?RfKZ8&#c(9ArT3e&jkA z807Rror|aMx$@e=fx5Y35~zXg%y!^R_D{Kzxia*GC$UA<`Iwgr<*V|xZ&=cD*r%e@ zH0VRpk>&e8_4X%-iyoPDc*0u?xjzJmZj%tDsnTVCg;_X^p{Dk+!r*VV=qtsBQ-;T( zGWIq2JtV`_RE;i3v^9Uu0zSqdvwSHD?i`cJ?zo zM`?xN96brKuKZJivnW}vS*1}rBs|ym`y3tu|HPnsVwb+~JPsm4#7;I@A^dR%4L!j- zr|+&|@IOhK+V=k9=u?@D=(K!8LXUHsa0LU3%Pd7rgb3kjL`)Q^O0SXs=cp{;M^}*q zpumDNgdpt_h6SDS7ezj5Yoz}jw<6TdFX}%xHg&e(OrY5(w1dxdk>9cO^8fOR61`z< zEOKrC`B0CGd7*_kZ}j zC7j^KKh_0O2ze6QF@Fw*tR>ygQcq0q9m0uO49z~PaizQ#(n~R+CW72@p4bjAbC;g% z)JW+fe?G9CLfdmZc&f!k;+Lc3Ok={Sn|ek4GW#iPA*rL69G6D^&J1Ax{fy3#IISSP z%EVf??WctuJ3ZJTA^?M*6fyD9oqfa8MBKlUoNhAA2p$5tqUfg?2dA0HmiR&!8Y-`N zw`i+3O+IoVr7g zREZI?qoZ=`aSYb*raT5QeYab%V*5LQeFFREA2AY&Y)z%EX{w~m^uMlgY6 zQuW{o@si0psaFO+m7rI52_X!IkSbfr$}B5j&;c@XE6zbPC>hTaw*!<}gqpo$&FzDP z+&~q_qn^PCr>Z;|%>TSo6Y`-zB2e;O7Ie8o>D%Dg+R~XzFe})giKR>`_l3h>1Be~N z;P?9+FG58D72b@a95`rk>zeorOg_pk&mrV_FOz42$eK;|2sah#TM+y_)I;kyO9vSddeB0KOH9U|X(vY8(CE zM$+yBB~exDaxB9D?(JFGV_fT1sY;ZO40gB{kR+SaL}Xl)7Y6g>!PYV8#7z>BKemiT z@=3TL-#6N=&u9}Ks*^pq25aO7L;|)-y7ajcejJI&r|9a+BaQZm({jg*TXXwFrBjC- zX8b05AiR6UrjuUk}|c`XQdZ=5MIT}B1%#3xh4F@rH;z!HZ>Q4EZwjDN?H)Jzoc zPr!2(S%jA4FOX{#CBay*540Sw-Tt>9M5fw(L)aGnlNn8fk4|9t@9W|pW247J1xBB7 zRI~-{xT9yV8V5L)29gSyus|W%Us4N%C|&V zXe|4Y5{IW145d!8C^WWT`vyJ{%X(&^uC93`x4Ra2hjO-|@++o@iDU@Fu3)aOT#2Qd z-}kwyX}Cy%^b};J;_mqLhorK2?MvpK!3P{Kvvs@ZSz^J2tF>0?kVQqn_NfUshgMok zXti0~3(6w=w}5QeariznntkLJAYjw+N%%^quo_TAZ`ktzYT_V5?439rMjV>LOGT=ps+8BD+-{FqsC*-TJtsEFDLH5q=Qx!t3I7 zT2Sf+`mEeDyX~hBr|&MxB!x$@m^QoBBKoU0;)vrI&ul{>*X8AIk%`f2tfgER53x^3 zXE$y`X(7JtF-XBCYIeh7Zkem(o{b-;`bJgs{ZvNoDg_=uMpw1*PXfRkcc-k zOvLZKp#w9?q*!}cLF_}Oe0IeiP2gOH`RhT+PWefAS6WgME?U{lSi72BAz6E7<%mND zTyAJXW5e;|D4|hYC;-fTyr-n}sYL@J1sK4j0L3a(9BGkmfoOjuq?oMLmA?-2P*38} zkEKn?8eygvD@t@fPz^y&wOv=T%Wf#!81yp0H||uO(^SkWc;G>z&Q-M6V7NiZrMIbk zJI9Sm$gTOBLXBl2L+|GfUu|Pso;@nIFA)luG)`^&Va`eXq2?WnN?qjo7VeR7mn5wc z<%ty`FVd%dH45)F%rMzCm~vzJvphpW${n$~YpL7=)2+=kt-p%IG}Zzi67m?f!v~wX z0DHdMj)j~K<}cz?#%fcnO_yubG>i&%<8TeE!wmu~fVo9A39!6zwpjcVDW8(`NFDLQ zohV^%)zcxtMD6fx<~gAr!7sN-v8LC=N27Wq^l57ICMT10>WbDXO)Wj%J#bvt%ou(z zUmo9a+^c~w$V}U(?iH*vKdK|&gZfI{AR*~X!t$4u?dZ{TLQ}WmBFAuVzm9}R0r@d_ zTC?!@s_&eRE&arlWzmr>wft9rdFHL^fo$TNzpqhP!nhv|x;Dfk^>>)!k!;vZk(u-l zJ(B$^5*I(PyD@{tidI%LJ1Ty^i|N2EG+EfWNJ`x&ZfWdWx33ayGS7y+(jZ$6eY$&b zetbNfq0pmchUNkECC1rPQY^zjosfV&D-Teruv1hGC9ifOz685_2&t`m);~aPW9RzZ z(wYVA;{ziDgqPUVBMy{c3zMx-RtnaNy#=!{vDrxcr%#G{mlY2uA*8(0AmQ-SojOo4 z9XjLwe3z4Z9HkiS(G#CS8_V(OFp;g>+I2a!TguBTADD*n-QnN8wW_=x#%b1X%ZRSt zQOzzkgBB%*BH-CeH&Vx!p4*^McFZ;v=}gistmx+|%dcw7M|ELph~0Oav5$`XZy`sG z$be^lyFWk&!oB&lnG6`EX73nO`sDx}nk;#bCq}H;zTp}g!ikdt>|=a_>l7Pt&?47> zHi@JI3~nRmn99DA(Ltw#Z|K+Egqk?~!@1_@F zhoC-%NS#m9n?R*-a)G#kmX!Q}T*OjPMrHFwBM_|N8j!_B zBsz&GIACB;@*&kDuToa88&4zy#3@uf4VGcB;Jv13{4Z$}9F&+|meC2YmdLmn`R68$ z&Nuqft4r)oo}#9NOvq^b_IR(7(F3=u;Du;JY>}Z?3o5#XFD3nBBkq6eEZ9cAu|`R>TW?|ah}xG0$=j%L zGOgX=9#jx|$TyS%_-o$+MjT%I_A_*Ow>}*V+(zSG`I8Rk@WJJT$Xrnfzsh~RBIj-a zAP=z)OT=&1Z&w}e;&9IwZI=kE*%+Q^HPFSFL{j$qBZoFBWSI6rQpE$!d8P7#!16(L z0}b%;W$&aiLZkeu&J`JmW*3>{QPFBFv7Qs_Et3#nSm?t}XP-@i21)BvQn)Z5K%5?* z!>0Ji@`6jerRu&q{E3`#!Ln7eGf2l0=C3YB4^;r!NE6>vOxnT1QhW1#z2!fFi+h$0 z2$(xNu*aBmkE>2I&Cbc<{OPBHosI>c<8P!V-E?RN?qC`jx2-$-VL(0$`VQ&6*R|7m zIv_o98_^SffXEi}hFWX^C;5o7`~dExNMIHuq7s~v-@{Iy%#xMCBB!?hz?6|AiYoxg zo9P6qR)@8N$mj@}MROfqEK?#XH+M&E><&od-l9~$CpFj|A~vqJt|>>P)hBBJ>-_pB zGtVj8CsqQ!@+61Qb%?-2rN{k9z!a|#pqOdcm!+(`xmdVx8+E6#y0P3+bau{){zV6@ z`Y`%Xr4E02jrCJ`LQ~_+GQTg^BNX04&!_3M6z77Zl;|wRH)|~WVtsA}y1i5dEgNjk z9>O*Fp%67bP1FGzE3JsmR;fb)S;rJ{y?ES<*xM4{PR>}@W+!cQzS!R#a^*5hIVISo!{1f zQe>bt$#~wsssE#Pd{jNiZ2Tm(45#3i>;_k)0d~KXn=$K(Z}ImPVD)_niYfQ}QWZ}# z8yGX+^XvWqDMiU0_{=<<%0Ww$QDuT(s#`Mr#Ib(;aD|7)T2{OpLYyiu$z9O+{dYz~ zX5|Ac@!fF;@gtTl@p5$1hI)Zfv;HMxvkjB@-Q=&oRK~We8#jiQp2x1kVozVw6_udldW5w;vtcGrTB z9rL@0vpUPZ+SeWmLP7XvYQpLavwttYAv1F2ahA0_rE|)>G?0Ro&aR zjidam#`mTuM}sQ0-U}GXkB?7t@0I*@&keG$Qcp33yG~Sc)Cg=@n6$Az>++WFf2N=P%qJ38`?uXXvC4 z`A(Io8Es+{9a>}NHS*L6-p*k->3w|D`@t%xjRTiZV{y;`KWk?|gK`p=vHb3oLOqmh z$;aIK($bJE-lB_?;>%>pYO`>yZ*}gs;R6FlN+FLS8!OUGtvF?p+)7+uXoRW3G^w5w zAACkVn z-j|_KST{I1Nb;hEvdq@a>(wH@-^%Qn(>N})<==SBa56amF8|z);&8+exl2Q)2i-_R z_-m5ZeDuPiY@1**-d>L@#5EqGle8;4M7ipjh{u={hGAaL@b8RgnY6OhL`xaP;3!T@ zl4PxKlA47#Xdbg3dhi~-vZa@hHUDCmHL2#BoHHdws{C|_YbMpYU>8B?56;S!m*;=} z+^+KpSIYtKr;sh@tW)@0DV`8?nYjnaOk&MxRP%9~+;t|(^Lit+%l(+``zTIhxp$-K z;@VVxm@Hg%zH$u=eYEXS)iS*G#h#z?uFcNm9m}T?6&}xPH^v%H@9uSYn9Yn9BKlVK zzqJZsC77iH))!!nXJCWTH@Aa`!cHCckPk1QvsF09>sM-jKmPe@WW+Oc^yOmY$qj>% z9LU@}-=A}lpzM#QSN$(*d@0JW5`=qXc(osWb-8%FqOyNcxiD$|t9ZJ{(xrO3U7hek zwp7Ki&uNs_3>CZ%eYxA*)V^L$K8|ohcX-Tn;((n*oc&2E=gPR;^Bdo2V6w(=$%bj_ zj+`0wVTLb#UZ({^CxhqikvP(19fY*Z*Aab++fxEO`RifVXVxx)1hlz<%VeEv3}*MQ z6!jn}VZRA$9SyGtgNe;6{&x%P{z4e3j1PRR3U3zHK)|#Bac-)Piod!OwMLiBO@W%Y z9SW=uORSD1*2pnUr_xlFmJcS&1=t*7?JJtcdCgh5quR8lx8!l8=HjTmocSwV+9UsNTDhLU4!d%VZ;+~ER!~IVtLg2a+My={>ht+`v8Mftg z8Uwxg4>QBv4hzQ~vzri$CW#zg&u-bbKOD|p`0QZJ7G-%wpLMR&LlAoF$0AzRiTZ@E`Xd1Svm;Qu2glJG-ODy&7THw&*<8?+nEGmsKR1-?G?;;E!towCZvo%nL; z>|mO49fJJC!Fgvv{_b@RCD4F9rL+n014~5%KPv^g31+iQi>1ZX)-1`ImJEN#dA;Il z_sfV<@g1U%PL%cMVWEX^R`9%KU%5PLKT?b6W4YLs6MaD*5~UnVm>eVMw)j_YUPyQ( zLP>F_9P5lcUX5G4DIw4*A~DmkL%|wHET1&|eQxvxl!%k>NwL{STXYfUJhNI=sH44i z-uLS=o~V|G7lqQAR(Y}ht`Eo+TC>Vp&WMUBWZM(hH|F>~=e&6gBhvM!o{g7KXOyQG ztgJVGnslRat!AMO0`yz|m=aXoHx;G=K$&P$A zd|L7@1usko9=kLfr|msE?`(Z&`>tf<5^b2(XBuMWX-0XbpFUeg{q9@*b#S`B-)fLG z@lI%_RS4x%TrljleQ{+2-0hLxXW~JF-gh1}CLZ@#ftwYGRO`T9ZRY%!LgJ^JGfrW8 zTNqV@+wZX)-T+Dxkv0bR!?t5OnrPJZsRF_vDd#;#l^&^Xo-MW4;G0@iWn1z7z}gVm-`*&9k2* zmQC@EnvHhvB5-dlP=Vz^=H^?wWM}buBNj~tr#ihj6NmR>GaMH$4&1_~DHQq4CXc>{ zgqn@X#b?qz(kaEsgztOz<*P35GXi31ysejfnw6IK7V5*#*Q|EG-=3Ug?F>BT`oPGU z?u&iGa&lF*0@D*t!EEp4YaA-G zc_$0i0>mnj_sLpk!+=KZn#t#GYC^TYfX0~fJ-PRWabM#SGMcQVKA@s0BSK#4G_-6} zeDG58Stu8IZA`QyXj4Qr57EGZxZoi9hlC==EV7XwK&=!uN##tSqgEN&vWKAgwqJbx znn6_^r2qH37g|pb=9T~MR8IM{|BUcTjsM4|8U*u>rXX}t=6?%mtKWxKsoF;U52EWp ArvLx| literal 21387 zcmcG#WmuG7^fpRLNRD&~I72BQ-5v7GkVAJ!BOnbTAs|S@&>-mmqI5SRB}fk45)#rS z4QKql|Mz@4=eo{^_b?a3to7_?t+nrUuf^VbW+FA!Ul0@05n^Cq5CbZTa10D=0R0QX z$3|;D><1}hU|^P_E^lvpc>4~I&&{r_jnDj=SX>+!n;4#%c6NQew!i=T?_PC%Ygtvp+V=7M z%2uDGSohFiMSY#Eo&Cbkv)!FPt6z(ci}NN9e(jD9?$6JLjep%7=;_>=I5;@}GkUr; zcydzy`B(1=Vy1Q%5wX;Mv{R7bGg7damU$m8F`%N{@Ox-rcRp~ecu}KmXXW>&rH&OO z@~R(mZ|ks&i*3r!-^{{dW$5h7ukNa~{>=I2`S`@>$%?i5ruFu=ZhoHG?$evolP%P= zuAcr}c=%!Wx4UVH5hKIB?Btt2>svLI7e_ZkTAGtCE_ZX9C>xu#S3j`ax_Bv+NKryi;qY<^zx{DAb++@9x0TfXrLLB&l4#q`pk{kq5xA6)Q+e;IB{ zYKjTxQA%mD$Ua>);CjH08W2qxz_<$LB!@Kvq@L%p)XyQS#whO97ZKNZMvv{_n6lKB zU@M^E!~Ehcc3HeY2}a&S15g#xxO;E^?%h;hh7JbBPljMitD?UBU|azV=Wcxq09Urk zX!GTyP6{RHaA|@>`Ay!q?x#|+0?bxKzpXs&Tc(Bc=bGg#^|8KhK-$t)f`KswxE3*qSiA)SEcCC*o<$ml$!?5mQcRL~D@%pBkzf)*pnNF)557K0w-LrCrZE zQg=*`U)Y_v{V1qp#rnCY9@~uQ2V8u*5w_QxQmNt6UQ_JG`oz_Ffl=f&*%ZD{0f#c< zVKj6}iw@BOc=@fppXu2#pQON`NUEd;M5zNQ3(h}eE1oz+6l!imXTG>k>6txDF{%Zm z#A`SlT-5cj?&Ic76E(wc3G7rE%slVlp3JNGMKMXOBkc3}6|*WL zV?MVogGDtqEvCf!fx$7vKPC&R{(Nq<{kqqX(|EAM7*rwAme*wnG~kH(8*9CB5NRB~ zrk(Yco7SFA|JAo>_m=dy($xz6vOyzM9l|3MNrq(Uc zy6+iEq5Su}zXF4gA{%0@F$JgQXx^8b}iL7t(nFZ zHDq=2YN$HMp$ExB3PNlzu=1(=bFULh!VdXe?@XP9i4>j*k!;v&RVEM~dpMsgyL4fy$!H{+zBOl z_Ky$$Cz$a6&aBvC$Zx;fzk_?El2HP8SpSgng`=pbB1~hHdvCQ?sp#)B1Ro4X0h}3Q zm}kC=4j7E;&_CV&K`nl~?P0%`MXo=Q3a1`LBecK(`yCtJx)AHo!9torr6LB z5uhQ$p@so_6Xo9;Yuo)K)`I1PcZDS?detG`s}FLI*c<;r+lCYzHgewT3)m zWg{y)jh5!Xn`AXg^oHQybS|2Z3vUj7-pFF; zhV$Tr?dSmLM&zV;-ZWT|-2YdEhuV~$-H5xNh(=%Ql z$@G6h_u}uqZG#&! zpcU(TkAyynWTu!B5p(P#L65(NxB=AAfueq^uOB)?i4y5b`iZ(b30b3{7)??hH%sy{ zux1@ZOh^a~-t6e77Zh_51X}`(vB_`-!O{B=kzac*X_cgQ&`f2qspnCanl=M;N#7XQ ztTRq%Jcf0*q(^FGvTb9Z9;(h^?sm=Yl}FKl4aI(2KwZ`pEknwMnbZXwR9%`VPz*>0 z*79$VJ&eCACV(Z#Qn2%bW&yKPTo)Oe>zEXoqzxw6`ocX1)NSOrs0D!p4OjdJ`^lxz!S%FEgk z4ai^R_HCJeyg+4&_r^A5e~H>A>imQ>c_QT}Yp==##emzTCWhzpVNG&j4nw2#6lM!L zkL0&t{`x9JDY0DmbDgZr1b0cOLrgDj8VvdcTIl7&MsdONlK&sJVG4~k(&Vt@w|JvJiSn@Lu+yb%Ev96T1yC{UbN*5|JTCZHk4xQ6s#Mp}|xd_+j zSbcJYX23%Fx^@s^BdokirN^oD&;H#C)8j}OTK~~JM}ybJcWCXlDQfLZoo4rY&m&o z=t!p_2mw!PPs(aYL5S_YLv9uruj8|mJ@11(Zqg5}nV<|L-eO{pl^w#Os#IbhO5Ys- zCaAxbvHOA7VIBjPz49*eGBoE(dfMT9ko1^?u-Ph z$vfB!)rW~un@l8&Z&2$*354Ixz~1xD$&bgtt3O*t*Y*T^45v-k5l> z+52sWA3QUwWv_~vNIn^s_=CRrMIOsobx51~T8v2Qx{Y!* zWpI*pB}a(;%__5h?TAjNkT2UF#?+P$in6^1EGm|iag;pn0T|IfzE8dUFcbs3%0VE6 zLNF+ja$!)dVczo&yA?GEnk6p`KAm#`4a|+NZd_3E5Kt26WB1a4xrV{>3#Zli*#-T@i#~wcD?x+c8si;yB zvs!r>BeqhwFpx9@D(@8dmw*E}im^SnSGN(#_>Jy%7x9k(t&8Xk+Iv9Il$I_fPwRFe z`A_n02r&3Gl+rEWfKI;&s735Q>0c$p3hRSLkb7NpZouw_#Xrf_XFft`&IFK5swZ{o zhE!#R{*(T{8}{LHkj}mzI}+J5UpNr|SFlrC0nn)Gt_U1iZS3by@t8~Sx9lY`vZ?1t znWk4VYO&FfaRB;m435IM(XW1RI)&BgCcQ#^_>}lAit#~8O~wVI7EPL`1DjI*hjpz7 zc=iP)P#avOQ$#h0Pa9C-yyTLOm#1pQboG%46MB<|sQz3wUG)Vc8Kw0qhHaZcvVg@@ z9_Q4GP0pIX{M-K^2g*E82xcS?8Ghz0u@q;BC4Z5aB8va1dfUt$h=GY4BlIG= z#%vRj!Ku%ElxNBE-?_B{OFavI5!}u+DE3$ZQ(Ud-?d2;*RxZ*3>&ak zCw>)?y>i5M8)Vce{_o_kHM1DTP**XW<93OfCAGCFwLh-}GA93owwd*jxnCaNOaX^K zvnKq9L)hDdup(n&VE++eRMXa%Ej0_Q&lBMxAe<7nuxh{#111OqhBNgHS)bLI(N}!h z=6*z=SH$CjgveSfL5C=@08x3)Z6b@v4UT@!uJpa+%IZ%!&ZRNR-oaZs&!>J8k9jS6 zdVTs3!A^3o+q6IYDAhhk4W;GhU+CExgPcrej>;l>7!)KA`t5Be)zw@8ew0|4{87N0q1L&(Mh^VB{f0Fy+Z&FyY zDPWU?zRCrmWXJm_{cisGIF+LXF~EktssrUxC;3nEN|Sk}*pNcS%MaemF zV^oE*2Wq@zef6yh`ae+X!6C-I*6*47!SXn=Nk(c0v60#oFSw=`#n7{b!aJ8z$ zLgas_mfJGrY@Jqty&MFK5b1ntS=O=^TJ&FtseRwOFJ?b^HYQn-^k+B$*8fKVXoYZL zNa+h!NA=r+e_b5bD@c21z^lqi9?WOp;k!W1e6I1EI{Z?$cbwzaI)oq!AcqYs%FsMWQ>o9 zIZ)JEvf4ireo+2y)uWm47pf+v5->Ral(!mkf)GbRSXQaAm+yZAmPx~CEze5aqi&)0 zsn#+kQ7$f?OPI|<5mMzFi=NQqXJgMGET^xvFxowQSHBm%IP_=1`BVAH`4X^J;&d=a z!Mg8O=D8r*nCSG}Q?>s8Nr1Oj0cRT$1{KG=|5$f8_Gc(7`s}9n?X+)Evd%~5x1Mfi zRLfnCw;PvSHU|HZE_5=GZlj^uXw&Q;BUdbp!VlJ033LCE4Igjk>wzxH@iqYZ=TQ#% z_{&gq>db(xyg9Z#mwKp+udU2G?X<5B*M%iv4#N__TqCbDcChsw; zRTe>FYU}nI*Xk~I*-R@A_ulKd4O3=8S+gCT3JpRdf2lF*YDbG6@`Q7YW$8Uh-6{8- zWLu19kqW#xytugg>(YAc@)#3;N<8Mxu}(6*OHR&*s}1E21GqUkBgbP_A!M}_*0vyf z^QI%$wntWNdoNLraHRW#i+5w;@Bsqvu-|o21ZHkaxaOGoSZ}+hIkP-b_2(V8ch@b~ z6*I`bmMUal#8HE5Eb1Yot_#wmLfTc>tGE@}w!X4WK>PBml+Bd)fH*$)%Rega zCIOa7divDqa`qS9G|X;$^h;hsojF_Ai+E!X8OoA9o6*Ma8$Ir?)aK%jVzN$8NRx8= zG+Z8=aRK$J7{9RR({$>(wvIP!)5)9xm`FGoC&a-XF*GzvBoeD*K7*X>IV1EKz3qlz zV99HY2;DtKx8v^HmdEGqR!K}QxhaAH$8bQIm`R0L`x^0a`IT_$^79s2KVjC2f@#&M0r{ zk-fzWUw;Yd`?bG!J!+t={7=}FBhYH@H0W+FIkka;%2NPVwL*gbk+r1RL-b;E2ic(C z!7L&}fnG0&l&NED(ewpS4T#dX+d`4!-(=-E-dV#xHF}*}?VU z!MR;1sw>mg9xD>cUOF+R@mkp*5mR5?%60ORDYHd=E`Azgb=;)Pvmxw)A5o4S>2 zN}#mO~&`oOirFM;`|KUJ`BM|POPYFFiMHQiF|L)6@{!`74lHk!2Y z2pVdVV2UYZRk=b``1UR`pWnz@H@0%M0U<3ZRolUtZPnh?Y_enY*2m}O@)oM1{~S^` z6(EJQ`aAeJ2D2OV?YU=q0V^$am{Aea2mFNZi3Ca+#L2Z38vSytYn-7?8wdL&dl&l@ zCxh-_#=0x2n~R?GwtJkr1sPfmql7klf z;Ef36_AY*iv=Y3u|8~3pEGapy1>LL&5Ise2+b)aLfXC%BlFRcnqw|9i9@EA*GBtY% z9v9f(Q~%ws4SKU#Zn``=U146~`e&se^WGGs#6{lxU0gR0Q%77Gh7Pe<7m#&TXgG*B zli6`5TufH9gp(A4sGNS_`Qg$~=5y!#7YBX3a`C}pM>Lf<2d5e#7M%P$G9#JlwVNQ$ zml+ixcoIr!f+Cny0~*Y8qyyfZ(r|lf!V^U?kSW|r@2D^h8SQm7lh5l$$9t_m+t;8^ z1x`%aA!_@Qw2SnM{vJNdus;D?WjsDu+BQ~RIa(i8GWwL+>lup62MG#ayGgHSc=CI} zQ8m+_s6RFEzVKnQx7>tGioowq`%(#pLg2zWvW4=uvL#@VLgwz5#++JVO&bXHA|7b7 zf6(db>ek(-u^re<)Z%S!pj2ssAJI3CJ$a!3Doapt*90Y&7oaOi;%N?!YiM~?5{<~2 z-1LrDK6mY)Sxnz`1vZ0PSZ5ztZK;~mp!^)8!C%MSr!>W$a*hu)Zx5#(sftH7b8H{| zlt^ku_SZD{{B1xM-Omr6o=B30=RMb#r+6(+&RU>?*#*|a#h5;@d#4C9K?F5(IJo)gG1KEp3!IhM8SwUcpX<#59Yb$f<)4J$a9*VHdY4a7ttn7ef zw*=R55LLn?lO!i}fyS1e1aCM>tW-(qaZ5ME&ps6KckG^`Zogk5YuCBY6kqT+(4eMrZIrPgiyBn}`qP3wm?OrvW{_3IGY+Z~Q&<-h9lnZ4PyJ z(}G4}-|#$qRv&JM2x>$=*ntjU@zLO93=(vROEBoqQ1SFDn7~7YAhXOrKGvkCDh`te zzNXNCvA(d6Zjg}i(nkcblkjV7YG`sG+H3N0lvqqSCh~YVi4_wHq;deJKjq;2eK!tV zZWzJ=%qqYkyAn)DUfkKp&!t+&T-yur`v7wsH37Z1;>*IfHDJppu!UqW|^Z^5f|+(51gRTq)|+q zjCsv;o$W6bC4>*yMiqb0{E)0mQSvuIk7@ZgMm`b8=5ws7Nd~oLL5b?0zThb2GewtJ zLTfFHm+1VV4;vy73E=_U{S=k?an?k}Q=|mc+pIhUY!fWGi`km3i$VtasDX!1jQq+a z87!YRf)6}>vW}FNhxhLuqMJoe@vmz`biZ1D4a7Erdm+vWoBw5r$Kn4eNnMaVJM`ZF z?f7CWhY3pD1QFlNd*0Nk5o!Kxo8k^P2N+sG*g8Lr zl>eO0hx?Z7V*SVcqclOMcJQ&I$-_ujobfd&E~+4ORsTJ7=N_L+{DPy?7Dc7b+J`KBe>a zg4U*`6zgEtyF7I5WcK=W2DKxf<3C1?PO`$&zh5H;rqQQ7SLSYj#|G@Xb?;fB+xRd? zv*;|-0k7XFi#@S5WmM1OH3VHZWGIuDK(LSkBVqn6^VMt2$L6|ZDQ^UCy zzcVK2cpT7>HzFas{|Xh2^nV;M>S8Y#Ttyf>fuMt1?wU9Czi4W?eHLj4M}u!@0$M|) zR-T}HxD#=%ZEG$%8pomG{g9Kkma;}9)zLLjx@(gAjr zK?F14PdMG+|MOa*RevnytQpbSmrNXJ{g0f}nT#o(Yx^Bffh;*M=TEOwXw}MDL|gY1cos`P+)_ZDq5%fPp*fZ5|S;b!wW-rlxDoB}7k1u-pTw(t{eI z&S5BcYE`;4Jr*UOmheS>%{=VO#SVJPBl*dvzfPwRs+>x$JlKfuuF@y|4hSL)xX4Py zTE@gj(-hB_v*R&tcl;Y#S`gRd&I#5x!PPya*#TG10N{&pgy@RVHo#AtD9UaVxc;+W z!0Ft6VyAUyfS7bS{5B%B_WF*qJ+U_)qLq%1F)2G^BAbns`Y#0}>pY^bueJ>b3!snR zNx$8QFwI#GYpMftS?N4F3ndS2c)^=E!NH&UU7$n`Pa8Fhr=gK66^+Z_<$1a7@OH-o z;0N-*8eXT+v>gb3)UcJZBsYO4krW|^&hN8|VMx!rp{$I^QybuQJ3gi73u|#5f#8DL zG}hE_^QKuO#mFZQhmfDZyd8ID06^5NE%$V^#R*@@z9!CqXau>zpaTflUk5eJmqrO_ z6={Jiy*Pi5LC$OdAeouBtdfSOL=4Xi{yZ!;6Z@mLI$j8DxCvu|$@mrgO|WaDmIJrG z6Z^DMyaG42iYw1f@_^&e{NnxYH<&tJZG_MS*eHSQ;zY=7&GE?=ZcBvo>NL~JbLSJ_<0-RqJhic5#$!z z4C;{M#93Hzdn--29Z+F4@v`1D_xfN697y%i4KUAh?;yjdK5uyo7=G=gsU<;as#UKc z46Y%I-(<)h6ynQ*vIjra*^+U7A^0k<{vo97e;7LJ(94^%an*DtkHA2?GzyZQj_8tX}WmL9|J(`JvS;Z!wk}hcwDp3R< z+k=W&-hXcC>@I0e)(chVRP(>__*aHv3y2U90waOK6kpiZrYKLMtmVlk(W@8zx7`0# zWdqnqtd*MKheQTGGj$0QK|WKb-oF|ToZ@`d@L!n^1(|^UHvNkS-EjY{2#mh}TM?8C z;kjoXC3bQBXXe@=f4TQx@ywyuEa;>?o`~G^dWs7>R+~PMIO>#63`C^-74j;s>I4}L3C&J-?E6_ zjOL_zo^3P<+&3AMYPbVIMxZk*DaX+;QWN&7M2WYly;`67F{_`TSR*7;gGW9a4BA_^ zw0P`@N(AnK(@rBGmtMe__A+_!(C@HrW%7$xUM1&EI}ATqyfPOoxTIO~!q0iz?PTmw zLubI57d@bnx%Gvo_5@glx*$Ep;Kj zXod~$<(5ZxOxv^Vbz|@SA{$4V57?rg(pa9aD@A8hVG*pbBp@S#e>}OsZ1tnO!CF1< z$lBA>pK0S(A-q(8*}LEJIS6@+(0O*wS+1WVafMFE{dFx&vVm zr6=n2WtiWJisai%{Kg+T>sXlm_VAdwgok#sivhm(-pJo438zS~@0gYmq$=1dXLUd{ zjWt`%9wKb&nN&OVTnS&4ldWWq>@ATK1yvNw(|+MZ);ujU_C}2~q3!X6iF{;yN1*Jq zADCbu3h|DjXoeZ|JvgSx1ss^lgh0qDh=D4Pxun8E{0xHE>|1HzGU z<0;5n*Iv`jM5GT!NqCH=?P^;Q*dAIR)|x>2t^#p!m2(;oCZ{YN3k4a?M}x7y3F5ZF z#DDI)sQl4_pZhU$;@hshiW(F=(ZQ9l(uf`~;J=D(_zEV+VOa*-_(q1Pok1!+p8=+N z?CZ}RQM3YJsizKJWBI|OE}X2}DUqXE0t!RX3VS}Tb;`-aDYCz8aGlsV*c`wABIiqc zMfW&@Be+EEC($`ALR{SrA?|7s048|#%UzK3RTdlrYAB6f1W#@JrEr?x@+Iu^GIgC@ z(!0+PAL`#tnD~U8&aD{rQeIw6@O%K6w+BeA=pWvvv22S-_{4-V6TNqngj1)gtC?)! zHe!ZF=10e3N0&2v(;Dw-^nKm(`JrFc*f;;v>xg&Boi0@dnR+l}7 z3zeY;Q$MTT+ks!gGEdi1RIdW~naaCp1e~H@Eg~7k7ikaf$d$y~B5-+Odyq48en7W{ z$zjPe#D2>2@_`O?>tmMC-FN|W%0iQVrCvs=UatHWL`~e2&D41YIkHyT!8AJ?#nr}} zh=#5IG8?l8zE=P9MQx{%p7Rww!aUyyl6ysrZ?!GL9 z-y!jHp!F^XlFxV;8KKX&J*^bvT%E)nX!_O_g<|6qOaEw;`EaKp!?lZu&pYW5OPk?^SYnR+o6nDC~rqZUc6zu-W(`*hFZ6AL~4wVl^^C3!kIH zuI9W7@v7W)TA(A^ctxHnhsV_q`ULE;5VAka96>LM7$YlZ9_AL+Q*`@P*8`gob%4Hq zQqgQP;Ck!X_j7b*<;me3*ihVy$ZoG#_|-40;WC^>1UA$5j zuuV)FPt(jXTei6+t5|#IPI~e!?cbV}v8_h9jpY7-M{?5};qQ!HV?EDzNV)qAh3WRx z-mJ0F-rF93P~F!8_x;`r=vuVkA}>HyHtS}NR4JYoEh|7qL?|gBa2ziqYq*L82FBhm zkJJas!4)XMsJBpYop%{9dPC;Qo8S94=E)D%MBB^0OND#HMQ6|LX@W_{iNgvrWWqX` zitu>qN3$TEub$T{E1tq|$j6o!H)p9E49uT_T9;n?=UgwWsax_c<&N|PY%327P5))x z-u?a4$8|+wb`U|LfUs($$CD9x9Tw}lt_g4T?e8U!m;B!QWPW+($-#R&TuY0L7aE|E zzE3#XSGhY|H7!p!ZnKINNAH*NlmG1R#M>v*;gY9gafqx{%Rddg2o)W~34}98K8a+U z zNfB?3CskY?FaP9V8K0y&&A1%0R!aJK{5+SRAIttG4o6%_{I=g+NW5_8F9Y1`)bBkQ zTi7X829muV%RYT@D?6AjD@?9L{)I{&-iya+md*-{H_kp+n25SI^e(nB!WMMmF6QW0 zV`@2V`PtKF)j2j{+4Q^ z$$`t1>B3AXaz5|G<6%&&oVbBEb#t$M%OV{)G{0EcO@;{jET1tfpTyI0IW)s2-NF*H zx7@2XXM)y5$I@Q9#U6Dl?VJ2e2#=pzu9WwuBo5+nWSVbI+WM)w4;k2LtIdnMrbV$r z_Fx3H#)}53%+c)C@`b4Eq_e14XebiK}_?4YDML^vLd3sVh1Odn8Z5;VMXIz+so)T^@Jz~nb{y;ilG>_ z!|)U+Bb+b3#+rBh>woS5SUAPLGpX?0@(#7Z&A{0{=3KP?n&SS+$RYc$gQ3N&S=$`; z^_Lq===5~bQgHiQ$wze|WeGRWDmaX@a=S_31KkumV3omD>^G4XydCTW)!)`}Gg4zd zbQ3&@1F~xu9O6K)*$!O=5Y-;o`qv3KSBDqBJ5}J%Y2vE_EPZDnqcqSDNq-y5ufmze z-g95S|5L&xtOgV5vcOKb?3-=bsg$Z$ZJ50-D+1%M>;1|eko{9TcQ)tHW-69~%krk( z{4!(Ip~3Nt-+2FF9SMgHGCH$kT6h!5o+FPQ@E3w(JBi)7cMMrtp6Cgx z#}*=QQ^BZQ*~J4NcUIP!Dq*=3uPXJBv{JHd#5!P9(YJ=HAm>SnkliFTV=7fA)YY$& zoIZOi;J8k1%>S~tRL$b)bGO}ZUf3f9rtdvF172;nyD&Wkemg4t5!zP*Pd=4;o%fCi zECk$o7VByNbQ6|)0q(B1>R&Nw&h{}q>-ZUWWJdN8@+Gflm)EJm0s0drlqw;o75;u8 z2NkX8WrU#3Xu(6%lh(W|qvY_9N6 z5%sRRC3Yq2-Jcm@Oah6@Gu^aqZUxLQAMp#neII!oM?{}W7cTzBlIFr*s$|jDzs{DO zmDA4`l;BzMwi8>2Iy)JBA>>D-VK>TmNq_{^AWI>BDSx zwYs0#dbkMN*$n>=t+C4qIFO9bJz?wI4r1JLALb_kd>b$L{`^blI#lBi#e4xXIwA8HGMrUc@ru>5$TQA+l zcU23Yy8TEbe?}I34*~3!XC&sQi{3pCHXr%!7oxE!Ng&=(6+qVi_7YhP!H!I@1gW}( zB9*NRrlQnu3h-ZUMcl4PPYbqzO`TTRob3W<%6)$F%k1`@tT#1G-rlv4MwGxJZi57K zYEO;=E5>X5kY1f1{;0>m<&Hsn~7~n zJD-CHO1$y0mxXV>_=Ia_Asu~Edw+#<=&h>Wn(D&R9T5rEhw}tZc3qhd6_UQ~W3@+2 zfMy}v&so<8$I2qv9I~%8rq4d1I{Np`);dflHtTYXB62IAV!p7g;?IpW>TsOom+2{AjKh-!ZR&HbfBDrE>>fgrK`nfJ-h(3*8*I*&t^O1kJ z?(5=gC6I$%rq!wkYQ`7w!qXSCFeTj<)ClZ-6N#K?h%J;c(ZyuO`IR-V`sKLm8@86R zUilEo&)v1rpvIgBg>QI#36ccG+Jq4TiDYMmD*UOnAuL}AAHG5`WiWnSj07knibyjH zfH>fDl5w%M1|Pw9Pj|B)lMyUr3RVc$ejH&V&n>0Aee?%0l19m^V*u-Wg#y%7#pE2a zLtuO982*)yk@73g@((fZz?zl`@H0`J&moVt9f%6lxai(B&V)s>XKG|XK96sxc$%Meq9PEuGGTJAhIVG`10%Z+puw5 zXgZFbBgiJxb_tv?TSw79c?V8XR3w14Fx~*P3QOSEZILTq|Cd{iEP&Kn1`60&k6W;OAQf^TyU_C+1|Z$qYJLQ_#qJdc8_OX zYXEtxAcgcyZSmaOn`-gQ4u2mDun)e*$giZ0%uARf|9LKY-rTN&LB^TTM5ew#T{lBo zk5ax;GA39ldRhx$wEq;{kzcJQ7x(>Co+SrzY0CU;I)E(R(S?!3^0#hh>lIF$Gl|%~ zaE2QukxYCVO9y5-P(mdR>8+v?^L#oa9M1;CidDHApOYo=MM)98|NOu}P5v|7c7c~Q zI}vaLXMG&#Cc!6C|4j7$g;3~yLJ<(k&sIjF_Qt)4jV0da&4{{!t6fpk;-FgQ$u5uW z3q140tmTe-NokJPH?e(}+>a?=2K^q(t9(%9^xO!C@heoNiHFyk?44!pi&;jMIHuVI zvC8lhvR!-reDeWhgq;)mn=w7xpFhrJ^CoRbvJn97oUdWYy6b%+v41>R&WjmXI%m4t z$UdEp9L?`_#-I!mW9Id63uIL<;#3}zQJLVkjz-+HUqju}y*FnwPa__g*5|s4Y+R0} z%^^A2SgP#!-^ZBTB=_O>WaLcp4->h_U?)MV7Ps3R2Zg-6^<6eqZZy|hbCbl(+1g4q z`abq9^davv=8%LFY?J6mHm-n(!(C0wJ=&ZSe@Ps00&|ArIwt*l1lIV7d?i4?&mDd; zsno~fz&2OQh%Il8pU-E=QP})?O`em2Fr<*1f;SI#@c2tgs$-BW!zs>44gNZo*)R(& zh@Rx%$+4#!V1qtX0-4rA={k?r2u>!$Xya8wUVKTep`U@ zoLq*|XmbXSC-S)!gjjaIhbT*j4K>rpbm;wpwhZw`Bp^XJcc_X5D&UN$tJl-Vo%dxU zSN?+ROuy28-MA<|^`0^Gy^ZR7MJBooT3IE-)NlLLWVSK` z*7Nrvx?nqmhrhX{UDqQX2V;-v?6nq23bT_MWL!8EyhyCF)j+8Rym@Tt8Mv;jQB0C% zEjPs%h*7H#$*=+F@L%Fae{k`6IQeJ$Ov^YBHv=}8lGr<;D4?38cc_aXaTIr$u%I5x z=w^t3&6!dZSAEd*bsHsnUJ(43Rq-8}KJM-+VqoL7M(4qRKFHma1LMn&6fBD1NoVa& zp6YqVPkJCT^EVbjs~I}NZ;d{1MWhhsS_Ie~#5q%jzr!F}e!5IHDEsgcGo7NE(mS%< z4vb{-ZQ1Gr_2q`H3X@lTz<{TI45FG4nsHvZ-0yoX2c$F&iE@sc^8HOhYqj8da-2}; z{~mN%fV}U><#bR|>EVXbU1-8lQHUsQxVju)G~z*#&Wi9_ezMNe7bIT=6xT(7NkxVD ztuD$qyd%lS2D7z536O0=2%`#<3?a_mBQIp>YoN_caSg%3MJb-MT#?GjIM9%1|MOG` zt0q)`I__=j8WA8%e=@u%#;wS%UKxU5j&^{UKAtN~ba3+7yynRxz6SEZKji=SlW&!) z3@^M(6Qd7;vm`(!kM(X;d*I2?x;5()4v*BHpVfHhtow4fo%i>TpO;0)G~<`=sAsqH z)@FOotwr4Z7({i#7Ca?MF4JjphhsaR}r{De~GM zKtQU%agaq?*s1}F?j?{F4dMQ$DC0}5Z9?C(N(@^a{+t9fH(f9fi~Q4pOYl~!5I+0K z$KIE>e(rG)x1E$ze1HR2F>-Ypillj>wT#hi#uQNz5^X$X`)Sw5*46#eyFs?oO z91pq3RybvSz<>vDiLW1aSB{?jVwpP8mO!Wbhn}AsSLpY`>&)cixm6*f#bAhARwCH4 zLxzsZ?x%>>bk2f-td}xZCST{XelGu~{di7$7jCswUIX=($CO`Uy+-+Z37leXK6X2+ zpnk9XOuMZD6EB1J3Q9)^s`*>==%}hpk{%3h*#UL_EFMyt%knZt)HWqX7=`)Z_gs%; z*wNl_cq_~pc;bC(6Rk-lsWNhXK*PUSfhB47$!wAuq%_j<8`uuZk2?4xoJw)BYVVoR z#0}NQ`50tcg?Y$DzN9!;;Vz>1VQd1~JK`a;)Yk)16x##CdpAP*(6OpCkG-2_}! zX)cl9C1U@Eqi*OUZWW+c$D2GqU;XiJA`Ya&uPnV-WNa`3NHZ5YL_Y}iAcU!5S5&ao zJ8b~tS){}#_4C}>udvkaYz=k<_}n#ju@b!h)(U_{|m| zWOfs!NTBk-43<2g%|W05a1a>FaySq|EI9}umJhcs!2uD7O71_i!-IE+LmC_e^Evfv zF1;KC*WL9hAhXUWXYF}6k(bF~0ao)*wzn`eLG@Bn^9ze39dGMA0b=O)RNifZVRmN@ z7B%dcCkuk*Gg-WFxi2zkLu$aUU`bZ}SByGLxsJVmMno}K@`*ov(roBPRI{lOLo8d^ zN{%Lyf=C4$z>(_B4`?W(>Y&8r@w>i>QCh#GTK)c)MYv`MKkAYEcmn~!!e=Xy){AWgHUhP=gjSdRXA3h zr96+G&c)`@Ou|R4u$`X>#GqI|M@?{Mno^XNBH*<0PX!w=g0I1^s3C^xDYP;C0jlx@ zT+DEuEi!n(aA7w1EW|@A%#m^}gqEi%dgA9WjSDNOQn!l*ijTJ7A82ZTq}V`GF9Yp8?~G zG51dao44!I#&T^A`2-5X*d8w8deNxYX}on++lU z^x_0hi(0^vP9HA}(fk>Sz?%__>t~yikVn)!Z)Z>arOPW;yilD31&Y6MExG4JC&u_1 zLVi=xwHo0YXM!(Qu0x9xPhaQY_8K?ta2sM&vNR=F4A?UNYSkb!2xjAB?#6B1Co%qr zv}<`1waG}Cy6`ygE0zKfPvE$|rU?p19A*X1oSerX{%WE5nvdj64$B88hjB9}3%*BG z-j^W{4w*`4sLo=11bXiUqMfyTMb^+`Ze1bd$zlJuWDZ$bjHxR7LwjHwCBgh0CBYc zluDMS?)|)u0xxBgIb~+6DalzC^3$_8d@$+1H0#Y6tDjc2LmP=ihIaY9;9cq1 z|Gm8;;TwZY$*-^KB^v8jDYc5!8gMSsEYxKBlFp1%EDwCasreGj&4o$E&iPYSfGMid z(c+`cpX{zr61xDnGs{OdC!g!$7a!t*GOhJM_*RSRGu|97r*~(Z-aSSze77c(zE*)K z2Oip&VkzyqAA7anTmEItM9|1!L}hV;r>E~k!X|#;TfF#03LGD34?QrfvVp(joL`c$ zybcx-8pZ|+dF=a|$bXfDN&@hVpZIE9@-b8pOAQu>`@NsxlCW!6CP;ro`KTz&vGmz5 zoj&fJ?7UIIlPvL6rewJI8t>tX$n1twmoCA!zg z^{zlx*Z~p793>~<^Oy-6xK6E(u@*&a6YT%$<;tU>e!o8xmLwD(`RbhSIln)CfBw$-{q@}E zxzFp~bMN!qbI(1`xlg(^X69$X^RLh5`SZOlLilpxWu5mTP+J1|MZ%9cPt!ze{j0 z=Ly~dG`Fg4nUmLLzN`k%>USdw4vnPhBAzGj@INM8S;0B`=J-cGhyY)5U%A!;y2yqz zCTlhu;edvp&?L&-6z2x@!-x%liJ%sz*Ie@w}9bE>X}qX>bt0^^L|Ql**?A zvuOB-WFHKn9Lg(GNM+vHi=#bBv~;viU>TtB)5Ccz`N~ZTNxYtKSyVvAv%`D|IUUjP zJ@a%C)L7e6&bx`H)w`_O#zr(uy162(xA&xG-z^mDH#+QI{=;OxC?)N~7N}nM3cP}A z#zFqJCA_<~^f6+B-u^L6s`laG0Fs-{Adz-lHaXzu{6lq>RQgkN;X$*j=1G@*zq%*LHgVA;U(7=erHrWu8dhse~33P0b>Prl$eupY5ap`GM z1|Y>6cIqgs&0L}x0@YT%%M&ar^mm_%BMijqt8J7lK@AQrymn*0k$37mLM( zX>3D>6>=9QE_)IYuSEJiXNv458()9^K0x!`>UwqH{)sxLx(Dm7I>3NW5#J8&H3AP_ z?RN?XV;j>yT`DeJDw9PmI)_4&NW`dSBc-6cR6?3~Q3h8HMKaRh|eJdNMe$(KVTtHMD%(q}rlRG~KTaeArKPsDFu4Y6v_(U7${ zkI2ZX{{Cjr>_~Qi2`~}`Cie9iEvJ+Z1vc4p>-Ev3CIx1r^DAvrjfNb{Ft*BLAxaO1 zvw1kR*NPc&o(>)=iRNRQ%TS;q9kK9TDP()Y)j)vj&ee0Dr8uB4As%OH8*R`fFCqc z*dM<@T_0NVOrQ11p?_bEAibCkac{>2BN?|xHq$< z+1dCd_HVUpq(~B1QUW_;kgviJKlAo`EWB%d<9#OFkJ~U72K^dxEp&e`1bY_00Pot% z7`xqJIuUlVt6%I*1h*>tbZBqx@>tU`F6`tjKT-n0I$U4Ay55RMyK3`@RHwOk{-}#+ zcM*b$a|b5Il>x;SQ$@+^*Mzlg`BV+{<(I@UzECrS;-U#TwX|xa6T+rzp5u%0Bwm7S zJD}oB4_?%$C?`HPT#TJTdu7GYUd(E4mkM>6!`;g}E8|h8J6a_5a{RU&n>j<^S14pV z9R>NE!4vAj`>)h%U!!81;1yofnt?!P(Fcc$kHe67=C01Q;A*2%A18uF*#q6OW28yH zVWq`)!XQjT1KqpztzgN~J8ttsd6Wb)dZi8O?2YK$C<(xh@C~{rR3>Oa%os5b3>21H zz#1hmU}G{I#@l4LQ5shya6<=4j&1_2zIk>?D5-z2m9z9-{lnDKUEnA5BkdSv=$mel z_H1v&IKuCD7-qqKFAp4jBI0ZGdpRQMf?=gNch2cucm#5=D?3cE6De++_H}z%)b3lO`6O}hK-d-Q$}66lD5Ho0hNLT zF1uQDf!96yr&VwzRLT+bdr!HZw}SVp_2-2vVXJsp?Crkh-LLQTrm9K*Z0G4ifr6X% z`Ab*01H*|X8XDrbPfW)2jEC4_ri+zRjt-Uo0@4G{=kQ>CRGKaPozb>C$Qd6^YF;<` zwUwo>0O0fj9(E|2pyk{=vHhK#`*>(N`odL$=qH+V;zx znZ{~WmK+Y=T0=)B*t>;}c#}t*=F~3L22R}o7G~>JYdYM3+pfR_Mt#xdpuCKO=f=YQ z&R0@&4y;v94^T{Rm2LxjTfrLgC1%zoNT7%{QrLPX(e?iV!oy_6jlc$0%F6gssCsO@ zKOowA9LHW6$8CC;{Z9BtmWBNLan3`^J{QX~hF9QF)ycF2&68mMdN#yP(ek2o2Tv)YVoHFpd-jH0yFZ^onVRyaE&s^DH(-rtI&4GFTH>khG}aqvII9to zF?5!}sFc|(i{6#q_4m4GD7w;tGg-{xS0#&@zB5xu!W0Q*Zs`$Z>I!i68S7Doci(( zO7byy89Uh~*hvMgQHM9Pmgk}eqq~;iPYFVXA_*fF&l4xGZ)^ z{>^8_VkB)vTahw$ub?aAx_@NRRWbYunQ42)jeh^&OoD-|TM@r)!AAt$yc6}Xw6gJA zMrcMh8k-&niiq-26=ih@WcoT%_dP0zimP@)ATa$yh|k-=#1*+>+ow?tzzm6e)ihq zuCQAcJuZ*qr=2L0oT1BziMNcu`Kpd}reE*ATvtZ8xp<41wdu{+XG^=I&WB7H5mvn% zMX|wexJ1MGdYx-w7<aD zcY13j#ZeOqIm)rVz}|56tLNJ@=|4LNG`HSB`#VW(plt_O(;V~v5lV|BrG0{|^~@9m z@Xwan_hu>Rr5P)?bo)+mtISQ^6sCChf;BV{_W9{B+*C#o3rP|Q3GPJov!wFHC7}hW z`7x^OX=VDS+b$_eY`h0+Sm4j~-S0b&Iqs%)5e>$|#ERwT*$--!mIMRI^;AkN&9YZ) zK`zR~ql)n@FLO#dYAVDHn6L*RdZg(FMLe~%P??R;pe1=k<$9@)LXFkqOJ5m<1yH;s z`!cu7gPow~&gZ9luHMw=1RnAdM6;|?Ggtk~JHlc5|MvVyb zF$(#!DbOIlIOKX<8~W^}uH83&+cWwewBo7sTKnkGmdsK_@Zw-k`v?6uoDqjxUk+dG z6d=RYkE(fz5pP9ISyqH*H|?MMRAV<5rp_pA3EPMkvpZcaD+zByp(E= zRYts5tUYP?>q~H_Di9I^-1{_dtJ=3unagaw%c1uVOW~P6Ft02xmm#comEpis*)?jh z6w39Dao42ei5;w?fFn zL@7hln{;k7R?L>-p;mCmZ~f75sxH)UzHjuHgS@|^T*-m Date: Fri, 14 Mar 2025 22:53:29 +0800 Subject: [PATCH 279/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91AI=EF=BC=9A=E6=9B=B4=E6=96=B0=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=20SQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/ruoyi-vue-pro.sql | 87 ++++++++++++------- .../admin/chat/AiChatMessageController.java | 6 +- .../admin/mindmap/vo/AiMindMapPageReqVO.java | 2 - .../admin/model/vo/tool/AiToolPageReqVO.java | 2 - .../admin/write/vo/AiWritePageReqVO.java | 2 - .../dataobject/chat/AiChatConversationDO.java | 1 - .../dal/dataobject/chat/AiChatMessageDO.java | 1 - .../ai/dal/dataobject/model/AiApiKeyDO.java | 1 - .../ai/dal/dataobject/model/AiChatRoleDO.java | 1 - .../ai/dal/dataobject/model/AiModelDO.java | 1 - .../ai/dal/dataobject/model/AiToolDO.java | 2 - 11 files changed, 56 insertions(+), 50 deletions(-) diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 0c810bef85..b1a07fc61c 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80200 (8.2.0) File Encoding : 65001 - Date: 31/12/2024 09:16:18 + Date: 14/03/2025 22:52:31 */ SET NAMES utf8mb4; @@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 21226 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 21417 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; -- ---------------------------- -- Records of infra_api_error_log @@ -128,7 +128,7 @@ CREATE TABLE `infra_codegen_column` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2483 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; +) ENGINE = InnoDB AUTO_INCREMENT = 2538 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义'; -- ---------------------------- -- Records of infra_codegen_column @@ -166,7 +166,7 @@ CREATE TABLE `infra_codegen_table` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 187 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; +) ENGINE = InnoDB AUTO_INCREMENT = 191 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义'; -- ---------------------------- -- Records of infra_codegen_table @@ -250,7 +250,7 @@ CREATE TABLE `infra_file` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1577 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; +) ENGINE = InnoDB AUTO_INCREMENT = 1655 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; -- ---------------------------- -- Records of infra_file @@ -444,7 +444,7 @@ CREATE TABLE `system_dict_data` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1683 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; +) ENGINE = InnoDB AUTO_INCREMENT = 1694 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; -- ---------------------------- -- Records of system_dict_data @@ -831,7 +831,7 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1551, 2, '描述模式', '2', 'ai_generate_mode', 0, '', '', '', '1', '2024-06-27 22:46:37', '1', '2024-06-28 01:22:24', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1552, 8, 'Suno', 'Suno', 'ai_platform', 0, '', '', '', '1', '2024-06-29 09:13:36', '1', '2024-06-29 09:13:41', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1553, 9, 'DeepSeek', 'DeepSeek', 'ai_platform', 0, '', '', '', '1', '2024-07-06 12:04:30', '1', '2024-07-06 12:05:20', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1554, 10, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2024-07-06 18:00:35', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1554, 13, '智谱', 'ZhiPu', 'ai_platform', 0, '', '', '', '1', '2024-07-06 18:00:35', '1', '2025-02-24 20:18:41', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1555, 4, '长', '4', 'ai_write_length', 0, '', '', '', '1', '2024-07-07 15:49:03', '1', '2024-07-07 15:49:03', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1556, 5, '段落', '5', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:49:54', '1', '2024-07-07 15:49:54', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1557, 6, '文章', '6', 'ai_write_format', 0, '', '', '', '1', '2024-07-07 15:50:05', '1', '2024-07-07 15:50:05', b'0'); @@ -888,13 +888,23 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1673, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2024-09-06 22:26:31', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1674, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2024-09-06 22:26:39', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1675, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2024-09-06 22:26:48', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1676, 0, '未激活', '0', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2024-09-21 08:13:34', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1677, 1, '在线', '1', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2024-09-21 08:13:48', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1678, 2, '离线', '2', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2024-09-21 08:13:59', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1679, 3, '已禁用', '3', 'iot_device_status', 0, '', '', '', '1', '2024-09-21 08:14:13', '1', '2024-09-21 08:14:13', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1676, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-01-28 16:09:27', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1677, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-01-28 16:09:26', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1678, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-01-28 16:09:25', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1680, 1, '属性', '1', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2024-09-29 20:09:41', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1681, 2, '服务', '2', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2024-09-29 20:08:23', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1682, 3, '事件', '3', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2024-09-29 20:08:20', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1686, 1, '聊天', '1', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:26:34', '1', '2025-03-03 12:26:34', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1687, 2, '图像', '2', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:23', '1', '2025-03-03 12:27:23', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1688, 3, '音频', '3', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:27:51', '1', '2025-03-03 12:27:51', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1689, 4, '视频', '4', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:03', '1', '2025-03-03 12:28:03', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1690, 5, '向量', '5', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:15', '1', '2025-03-03 12:28:15', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗灭', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', b'0'); COMMIT; -- ---------------------------- @@ -914,7 +924,7 @@ CREATE TABLE `system_dict_type` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 640 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; +) ENGINE = InnoDB AUTO_INCREMENT = 641 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; -- ---------------------------- -- Records of system_dict_type @@ -1014,13 +1024,14 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (630, 'IOT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2024-09-06 22:20:17', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (631, 'IOT 设备状态', 'iot_device_status', 0, '', '1', '2024-09-21 08:12:55', '1', '2024-09-21 08:12:55', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (631, 'IOT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-01-28 16:09:42', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (632, 'IOT 物模型功能类型', 'iot_product_function_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2024-09-29 20:09:26', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (634, 'IOT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2024-09-06 14:30:14', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (635, 'IOT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2024-08-10 04:06:56', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (637, 'IOT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2024-08-10 12:06:09', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (638, 'IOT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2024-09-06 20:05:13', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (639, 'IOT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2024-09-06 22:04:13', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', b'0', '1970-01-01 00:00:00'); COMMIT; -- ---------------------------- @@ -1044,7 +1055,7 @@ CREATE TABLE `system_login_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3415 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 3442 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; -- ---------------------------- -- Records of system_login_log @@ -1175,7 +1186,7 @@ CREATE TABLE `system_menu` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2913 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; +) ENGINE = InnoDB AUTO_INCREMENT = 2925 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; -- ---------------------------- -- Records of system_menu @@ -1373,7 +1384,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1241, '文件配置删除', 'infra:file-config:delete', 3, 4, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1242, '文件配置导出', 'infra:file-config:export', 3, 5, 1237, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2022-04-20 17:03:10', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1243, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, b'1', b'1', b'1', '1', '2022-03-16 23:47:40', '1', '2024-04-23 00:02:11', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2024-09-06 09:19:42', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '104', '2025-01-04 10:59:37', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '1', '2024-02-29 08:51:25', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0'); @@ -1978,11 +1989,11 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2763, 'API 密钥创建', 'ai:api-key:create', 3, 2, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:26', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2764, 'API 密钥更新', 'ai:api-key:update', 3, 3, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:42', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2765, 'API 密钥删除', 'ai:api-key:delete', 3, 4, 2761, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2024-05-13 20:36:48', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2767, '聊天模型', '', 2, 0, 2760, 'chat-model', 'fa-solid:abacus', 'ai/model/chatModel/index.vue', 'AiChatModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-10 22:44:16', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2768, '聊天模型查询', 'ai:chat-model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:02', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2769, '聊天模型创建', 'ai:chat-model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:12', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2770, '聊天模型更新', 'ai:chat-model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:18', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2771, '聊天模型删除', 'ai:chat-model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2024-05-13 20:37:23', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2767, '模型配置', '', 2, 0, 2760, 'model', 'fa-solid:abacus', 'ai/model/model/index.vue', 'AiModel', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:57:41', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2768, '聊天模型查询', 'ai:model:query', 3, 1, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:19:46', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2769, '聊天模型创建', 'ai:model:create', 3, 2, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:10', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2770, '聊天模型更新', 'ai:model:update', 3, 3, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:14', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2771, '聊天模型删除', 'ai:model:delete', 3, 4, 2767, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-05-10 14:42:48', '1', '2025-03-03 09:20:27', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2773, '聊天角色', '', 2, 0, 2760, 'chat-role', 'fa:user-secret', 'ai/model/chatRole/index.vue', 'AiChatRole', 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '1', '2024-05-13 20:41:45', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2774, '聊天角色查询', 'ai:chat-role:query', 3, 1, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2775, '聊天角色创建', 'ai:chat-role:create', 3, 2, 2773, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-05-13 12:39:28', '', '2024-05-13 12:39:28', b'0'); @@ -2008,7 +2019,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2795, 'AI 写作删除', 'ai:write:delete', 3, 4, 2793, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-07-10 13:24:34', '', '2024-07-10 13:24:34', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2796, 'AI 音乐', '', 2, 4, 2758, 'music', 'fa:music', 'ai/music/index/index.vue', 'AiMusic', 0, b'1', b'1', b'1', '1', '2024-07-17 09:21:12', '1', '2024-07-29 21:11:52', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2797, '客服中心', '', 2, 100, 2362, 'kefu', 'fa-solid:user-alt', 'mall/promotion/kefu/index', 'KeFu', 0, b'1', b'1', b'1', '1', '2024-07-17 23:49:05', '1', '2024-07-17 23:49:16', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2798, 'AI 思维导图', '', 2, 5, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2024-07-29 21:33:20', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2798, 'AI 思维导图', '', 2, 6, 2758, 'mind-map', 'fa:sitemap', 'ai/mindmap/index/index.vue', 'AiMindMap', 0, b'1', b'1', b'1', '1', '2024-07-29 21:31:59', '1', '2025-03-02 18:57:31', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2799, '导图管理', '', 2, 14, 2760, 'mind-map', 'fa:map', 'ai/mindmap/manager/index', 'AiMindMapManager', 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '1', '2024-08-10 17:24:28', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2800, '思维导图查询', 'ai:mind-map:query', 3, 1, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2801, '思维导图删除', 'ai:mind-map:delete', 3, 4, 2799, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 09:15:09', '', '2024-08-10 09:15:09', b'0'); @@ -2045,6 +2056,18 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2910, 'IoT 产品物模型删除', 'iot:think-model-function:delete', 3, 4, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2911, 'IoT 产品物模型导出', 'iot:think-model-function:export', 3, 5, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2915, 'AI 知识库', '', 2, 5, 2758, 'knowledge', 'ep:notebook', 'ai/knowledge/knowledge/index', 'AiKnowledge', 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '1', '2025-03-02 18:58:37', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2916, 'AI 知识库查询', 'ai:knowledge:query', 3, 1, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2917, 'AI 知识库创建', 'ai:knowledge:create', 3, 2, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2918, 'AI 知识库更新', 'ai:knowledge:update', 3, 3, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2919, 'AI 知识库删除', 'ai:knowledge:delete', 3, 4, 2915, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-02-28 07:04:21', '', '2025-02-28 07:04:21', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2920, '工具管理', '', 2, 0, 2760, 'tool', 'fa-solid:tools', 'ai/model/tool/index.vue', 'AiTool', 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '1', '2025-03-14 19:20:18', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2921, '工具查询', 'ai:tool:query', 3, 1, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); COMMIT; -- ---------------------------- @@ -2166,7 +2189,7 @@ CREATE TABLE `system_oauth2_access_token` ( PRIMARY KEY (`id`) USING BTREE, INDEX `idx_access_token`(`access_token` ASC) USING BTREE, INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 12055 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 13666 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; -- ---------------------------- -- Records of system_oauth2_access_token @@ -2288,7 +2311,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1711 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 1732 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; -- ---------------------------- -- Records of system_oauth2_refresh_token @@ -2322,7 +2345,7 @@ CREATE TABLE `system_operate_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 9064 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; +) ENGINE = InnoDB AUTO_INCREMENT = 9065 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 版本'; -- ---------------------------- -- Records of system_operate_log @@ -2783,9 +2806,7 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2142, 2, 1014, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); -INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2144, 2, 1016, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1); @@ -3305,7 +3326,7 @@ CREATE TABLE `system_sms_code` ( `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号' -) ENGINE = InnoDB AUTO_INCREMENT = 645 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; +) ENGINE = InnoDB AUTO_INCREMENT = 646 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; -- ---------------------------- -- Records of system_sms_code @@ -3346,7 +3367,7 @@ CREATE TABLE `system_sms_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1241 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 1255 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; -- ---------------------------- -- Records of system_sms_log @@ -3588,7 +3609,7 @@ CREATE TABLE `system_user_role` ( `deleted` bit(1) NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 47 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表'; +) ENGINE = InnoDB AUTO_INCREMENT = 48 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户和角色关联表'; -- ---------------------------- -- Records of system_user_role @@ -3604,12 +3625,12 @@ INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_t INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (15, 111, 110, '110', '2022-02-23 13:14:38', '110', '2022-02-23 13:14:38', b'0', 121); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (16, 113, 111, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (18, 1, 2, '1', '2022-05-12 20:39:29', '1', '2022-05-12 20:39:29', b'0', 1); -INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (20, 104, 101, '1', '2022-05-28 15:43:57', '1', '2022-05-28 15:43:57', b'0', 1); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (22, 115, 2, '1', '2022-07-21 22:08:30', '1', '2022-07-21 22:08:30', b'0', 1); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (35, 112, 1, '1', '2024-03-15 20:00:24', '1', '2024-03-15 20:00:24', b'0', 1); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (36, 118, 1, '1', '2024-03-17 09:12:08', '1', '2024-03-17 09:12:08', b'0', 1); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (38, 114, 101, '1', '2024-03-24 22:23:03', '1', '2024-03-24 22:23:03', b'0', 1); INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (46, 117, 1, '1', '2024-10-02 10:16:11', '1', '2024-10-02 10:16:11', b'0', 1); +INSERT INTO `system_user_role` (`id`, `user_id`, `role_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (47, 104, 2, '1', '2025-01-04 10:40:33', '1', '2025-01-04 10:40:33', b'0', 1); COMMIT; -- ---------------------------- @@ -3644,10 +3665,10 @@ CREATE TABLE `system_users` ( -- Records of system_users -- ---------------------------- BEGIN; -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2024-12-28 20:29:58', 'admin', '2021-01-05 17:03:47', NULL, '2024-12-28 20:29:58', b'0', 1); +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2025-03-13 12:44:59', 'admin', '2021-01-05 17:03:47', NULL, '2025-03-13 12:44:59', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1); -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$jDFLttgfik0QqJKAbfhMa.2A9xXoZmAIxakdFJUzkX.MgBKT6ddo6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-09-17 15:05:43', '', '2021-01-21 02:13:53', NULL, '2024-09-17 15:05:43', b'0', 1); +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2025-01-04 10:40:49', '', '2021-01-21 02:13:53', NULL, '2025-01-04 10:40:49', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2022-02-27 08:26:51', b'0', 118); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2022-02-27 08:26:53', b'0', 119); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, '', 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2022-02-27 08:26:56', b'0', 120); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java index b4fa8ab88c..bfd1e41caf 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java @@ -64,8 +64,7 @@ public class AiChatMessageController { @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux> sendChatMessageStream( - @Valid @RequestBody AiChatMessageSendReqVO sendReqVO) { + public Flux> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) { return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); } @@ -123,8 +122,7 @@ public class AiChatMessageController { @Operation(summary = "删除指定对话的消息") @DeleteMapping("/delete-by-conversation-id") @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024") - public CommonResult deleteChatMessageByConversationId( - @RequestParam("conversationId") Long conversationId) { + public CommonResult deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) { chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId()); return success(true); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java index c123ab70e2..f7769b4e6b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Schema(description = "管理后台 - AI 思维导图分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class AiMindMapPageReqVO extends PageParam { @Schema(description = "用户编号", example = "4325") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java index f3e01ee60c..dc8b04c507 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java @@ -15,8 +15,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Schema(description = "管理后台 - AI 工具分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class AiToolPageReqVO extends PageParam { @Schema(description = "工具名称", example = "王五") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java index 047380e422..04f99ae13c 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Schema(description = "管理后台 - AI 写作分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class AiWritePageReqVO extends PageParam { @Schema(description = "用户编号", example = "28404") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java index 358e994104..23aec276db 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java @@ -21,7 +21,6 @@ import java.time.LocalDateTime; @TableName("ai_chat_conversation") @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java index 94f764c85e..2364d750cb 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java @@ -23,7 +23,6 @@ import java.util.List; @TableName(value = "ai_chat_message", autoResultMap = true) @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java index e251d55c85..346811f0d5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java @@ -16,7 +16,6 @@ import lombok.*; @TableName("ai_api_key") @KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java index 4a65796a96..bb6a3ca48d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java @@ -21,7 +21,6 @@ import java.util.List; @TableName(value = "ai_chat_role", autoResultMap = true) @KeySequence("ai_chat_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java index f4ff09212a..b39320291b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java @@ -20,7 +20,6 @@ import lombok.*; @TableName("ai_model") @KeySequence("ai_model_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java index 41e5bcc06b..7773e978cc 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java @@ -16,8 +16,6 @@ import lombok.*; @TableName("ai_tool") @KeySequence("ai_tool_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor From 348c138749cbdc3a6dd874098ec1a1a46b2216e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 15 Mar 2025 00:26:44 +0800 Subject: [PATCH 280/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT=EF=BC=9A=E5=BC=95=E5=85=A5=20IotStandardR?= =?UTF-8?q?esponse=20=E5=AE=9E=E4=BD=93=E7=B1=BB=EF=BC=8C=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E5=A4=84=E7=90=86=E5=99=A8=E7=9A=84=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=EF=BC=8C=E4=BC=98=E5=8C=96=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotDeviceConfigSetVertxHandler.java | 23 ++++- .../IotDeviceOtaUpgradeVertxHandler.java | 23 ++++- .../IotDevicePropertyGetVertxHandler.java | 23 ++++- .../IotDevicePropertySetVertxHandler.java | 23 ++++- .../IotDeviceServiceInvokeVertxHandler.java | 27 +++++- .../common/pojo/IotStandardResponse.java | 94 +++++++++++++++++++ .../common/util/IotPluginCommonUtils.java | 49 ++++++---- .../router/IotDeviceAuthVertxHandler.java | 16 +++- .../router/IotDeviceMqttMessageHandler.java | 64 +++++++++---- .../router/IotDeviceWebhookVertxHandler.java | 12 ++- .../IotDeviceEventReportVertxHandler.java | 70 ++++++++++---- .../IotDevicePropertyReportVertxHandler.java | 82 ++++++++++++---- 12 files changed, 415 insertions(+), 91 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java index 5051965b2f..6d2b3b5bae 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceConfigSetReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -25,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDeviceConfigSetVertxHandler implements Handler { public static final String PATH = "/sys/:productKey/:deviceName/thing/service/config/set"; + public static final String METHOD = "thing.service.config.set"; private final IotDeviceDownstreamHandler deviceDownstreamHandler; @@ -44,17 +46,32 @@ public class IotDeviceConfigSetVertxHandler implements Handler { .setConfig(config); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); return; } // 2. 调用处理器 try { CommonResult result = deviceDownstreamHandler.setDeviceConfig(reqDTO); - IotPluginCommonUtils.writeJson(routingContext, result); + + // 使用IotStandardResponse实体类返回结果 + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); + } else { + response = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 配置设置异常]", reqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java index 0d52dad498..888677d8b2 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceOtaUpgradeReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -23,6 +24,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDeviceOtaUpgradeVertxHandler implements Handler { public static final String PATH = "/ota/:productKey/:deviceName/upgrade"; + public static final String METHOD = "ota.device.upgrade"; private final IotDeviceDownstreamHandler deviceDownstreamHandler; @@ -49,17 +51,32 @@ public class IotDeviceOtaUpgradeVertxHandler implements Handler .setInformation(information); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); return; } // 2. 调用处理器 try { CommonResult result = deviceDownstreamHandler.upgradeDeviceOta(reqDTO); - IotPluginCommonUtils.writeJson(routingContext, result); + + // 使用IotStandardResponse实体类返回结果 + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); + } else { + response = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) OTA 升级异常]", reqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java index 2e99a1b626..dc2a8acfef 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -25,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDevicePropertyGetVertxHandler implements Handler { public static final String PATH = "/sys/:productKey/:deviceName/thing/service/property/get"; + public static final String METHOD = "thing.service.property.get"; private final IotDeviceDownstreamHandler deviceDownstreamHandler; @@ -44,17 +46,32 @@ public class IotDevicePropertyGetVertxHandler implements Handler .setIdentifiers(identifiers); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); return; } // 2. 调用处理器 try { CommonResult result = deviceDownstreamHandler.getDeviceProperty(reqDTO); - IotPluginCommonUtils.writeJson(routingContext, result); + + // 使用IotStandardResponse实体类返回结果 + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); + } else { + response = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 属性获取异常]", reqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java index ddcebccffb..4f0afdccf2 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -25,6 +26,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDevicePropertySetVertxHandler implements Handler { public static final String PATH = "/sys/:productKey/:deviceName/thing/service/property/set"; + public static final String METHOD = "thing.service.property.set"; private final IotDeviceDownstreamHandler deviceDownstreamHandler; @@ -44,17 +46,32 @@ public class IotDevicePropertySetVertxHandler implements Handler .setProperties(properties); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); return; } // 2. 调用处理器 try { CommonResult result = deviceDownstreamHandler.setDeviceProperty(reqDTO); - IotPluginCommonUtils.writeJson(routingContext, result); + + // 使用IotStandardResponse实体类返回结果 + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); + } else { + response = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 属性设置异常]", reqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java index c4fd2e5044..3a52f212c2 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.downstream.router; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; import cn.iocoder.yudao.module.iot.plugin.common.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -25,6 +26,8 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDeviceServiceInvokeVertxHandler implements Handler { public static final String PATH = "/sys/:productKey/:deviceName/thing/service/:identifier"; + public static final String METHOD_PREFIX = "thing.service."; + public static final String METHOD_SUFFIX = ""; private final IotDeviceDownstreamHandler deviceDownstreamHandler; @@ -45,17 +48,35 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler result = deviceDownstreamHandler.invokeDeviceService(reqDTO); - IotPluginCommonUtils.writeJson(routingContext, result); + + // 使用IotStandardResponse实体类返回结果 + String method = METHOD_PREFIX + reqDTO.getIdentifier() + METHOD_SUFFIX; + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reqDTO.getRequestId(), method, result.getData()); + } else { + response = IotStandardResponse.error( + reqDTO.getRequestId(), method, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 服务调用异常]", reqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + // 使用IotStandardResponse实体类返回错误 + String method = METHOD_PREFIX + reqDTO.getIdentifier() + METHOD_SUFFIX; + IotStandardResponse errorResponse = IotStandardResponse.error( + reqDTO.getRequestId(), method, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java new file mode 100644 index 0000000000..a006f3a6ad --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.iot.plugin.common.pojo; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * IoT 标准协议响应实体类 + *

+ * 用于统一 MQTT 和 HTTP 的响应格式 + * + * @author haohao + */ +@Data +@Accessors(chain = true) +public class IotStandardResponse { + + /** + * 消息ID + */ + private String id; + + /** + * 状态码 + */ + private Integer code; + + /** + * 响应数据 + */ + private Object data; + + /** + * 响应消息 + */ + private String message; + + /** + * 方法名 + */ + private String method; + + /** + * 协议版本 + */ + private String version; + + /** + * 创建成功响应 + * + * @param id 消息ID + * @param method 方法名 + * @return 成功响应 + */ + public static IotStandardResponse success(String id, String method) { + return success(id, method, null); + } + + /** + * 创建成功响应 + * + * @param id 消息ID + * @param method 方法名 + * @param data 响应数据 + * @return 成功响应 + */ + public static IotStandardResponse success(String id, String method, Object data) { + return new IotStandardResponse() + .setId(id) + .setCode(200) + .setData(data) + .setMessage("success") + .setMethod(method) + .setVersion("1.0"); + } + + /** + * 创建错误响应 + * + * @param id 消息ID + * @param method 方法名 + * @param code 错误码 + * @param message 错误消息 + * @return 错误响应 + */ + public static IotStandardResponse error(String id, String method, Integer code, String message) { + return new IotStandardResponse() + .setId(id) + .setCode(code) + .setData(null) + .setMessage(message) + .setMethod(method) + .setVersion("1.0"); + } +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java index d60df9cc0d..2e09c3c5c3 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.iot.plugin.common.util; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.system.SystemUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import io.vertx.core.http.HttpHeaders; import io.vertx.ext.web.RoutingContext; import org.springframework.http.MediaType; @@ -12,7 +12,7 @@ import org.springframework.http.MediaType; /** * IoT 插件的通用工具类 * - * 芋道源码 + * @author 芋道源码 */ public class IotPluginCommonUtils { @@ -33,34 +33,43 @@ public class IotPluginCommonUtils { SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID(), IdUtil.fastSimpleUUID()); } - @SuppressWarnings("deprecation") - public static void writeJson(RoutingContext routingContext, CommonResult result) { - routingContext.response() - .setStatusCode(200) - .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) - .end(JsonUtils.toJsonString(result)); - } - - @SuppressWarnings("deprecation") - public static void writeJson(RoutingContext routingContext, String result) { - routingContext.response() - .setStatusCode(200) - .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) - .end(result); - } - /** - * 将对象转换为 JSON 字符串后写入响应 + * 将对象转换为JSON字符串后写入HTTP响应 * * @param routingContext 路由上下文 * @param data 数据对象 */ @SuppressWarnings("deprecation") - public static void writeJson(RoutingContext routingContext, Object data) { + public static void writeJsonResponse(RoutingContext routingContext, Object data) { routingContext.response() .setStatusCode(200) .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) .end(JsonUtils.toJsonString(data)); } + /** + * 生成标准JSON格式的响应并写入HTTP响应(基于IotStandardResponse) + *

+ * 推荐使用此方法,统一MQTT和HTTP的响应格式。使用方式: + * + *

+     * // 成功响应
+     * IotStandardResponse response = IotStandardResponse.success(requestId, method, data);
+     * IotPluginCommonUtils.writeJsonResponse(routingContext, response);
+     *
+     * // 错误响应
+     * IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, code, message);
+     * IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse);
+     * 
+ * + * @param routingContext 路由上下文 + * @param response IotStandardResponse响应对象 + */ + @SuppressWarnings("deprecation") + public static void writeJsonResponse(RoutingContext routingContext, IotStandardResponse response) { + routingContext.response() + .setStatusCode(200) + .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) + .end(JsonUtils.toJsonString(response)); + } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java index 3f3cf94e9a..472eb83f7f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -14,7 +14,12 @@ import java.util.Collections; /** * IoT Emqx 连接认证的 Vert.x Handler - * MQTT HTTP + * MQTT + * HTTP + * + * 注意:该处理器需要返回特定格式:{"result": "allow"} 或 {"result": "deny"}, + * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类。 * * @author haohao */ @@ -43,15 +48,18 @@ public class IotDeviceAuthVertxHandler implements Handler { // 调用认证 API CommonResult authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO); if (authResult.getCode() != 0 || !authResult.getData()) { - IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "deny")); + // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求 + IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); return; } // 响应结果 - IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "allow")); + // 注意:这里必须返回 {"result": "allow"} 格式,以符合 EMQX 认证插件的要求 + IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "allow")); } catch (Exception e) { log.error("[handle][EMQX 认证异常]", e); - IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "deny")); + // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求 + IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny")); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java index 9851d1747f..4af6877bfd 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.netty.handler.codec.mqtt.MqttQoS; import io.vertx.core.buffer.Buffer; @@ -14,6 +16,8 @@ import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; /** * IoT 设备 MQTT 消息处理器 @@ -32,16 +36,16 @@ public class IotDeviceMqttMessageHandler { // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply // 设备上报事件 标准 JSON - // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post - // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply + // 请求 + // Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post + // 响应 + // Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply private static final String SYS_TOPIC_PREFIX = "/sys/"; private static final String PROPERTY_POST_TOPIC = "/thing/event/property/post"; private static final String EVENT_POST_TOPIC_PREFIX = "/thing/event/"; private static final String EVENT_POST_TOPIC_SUFFIX = "/post"; private static final String REPLY_SUFFIX = "_reply"; - private static final int SUCCESS_CODE = 200; - private static final String SUCCESS_MESSAGE = "success"; private static final String PROPERTY_METHOD = "thing.event.property.post"; private static final String EVENT_METHOD_PREFIX = "thing.event."; private static final String EVENT_METHOD_SUFFIX = ".post"; @@ -211,27 +215,25 @@ public class IotDeviceMqttMessageHandler { * @param method 响应方法 * @param customData 自定义数据,可为null */ - private void sendResponse(String topic, JSONObject jsonObject, String method, JSONObject customData) { + private void sendResponse(String topic, JSONObject jsonObject, String method, Object customData) { String replyTopic = topic + REPLY_SUFFIX; - JSONObject data = customData != null ? customData : new JSONObject(); - JSONObject response = new JSONObject() - .set("id", jsonObject.getStr("id")) - .set("code", SUCCESS_CODE) - .set("data", data) - .set("message", SUCCESS_MESSAGE) - .set("method", method); + // 使用IotStandardResponse实体类构建响应 + IotStandardResponse response = IotStandardResponse.success( + jsonObject.getStr("id"), + method, + customData); try { mqttClient.publish(replyTopic, - Buffer.buffer(response.toString()), + Buffer.buffer(JsonUtils.toJsonString(response)), MqttQoS.AT_LEAST_ONCE, false, false); log.info("[sendResponse][发送响应消息成功][topic: {}]", replyTopic); } catch (Exception e) { log.error("[sendResponse][发送响应消息失败][topic: {}][response: {}]", - replyTopic, response.toString(), e); + replyTopic, response, e); } } @@ -249,7 +251,29 @@ public class IotDeviceMqttMessageHandler { reportReqDTO.setReportTime(LocalDateTime.now()); reportReqDTO.setProductKey(topicParts[2]); reportReqDTO.setDeviceName(topicParts[3]); - reportReqDTO.setProperties(jsonObject.getJSONObject("params")); + + // 只使用标准JSON格式处理属性数据 + JSONObject params = jsonObject.getJSONObject("params"); + if (params == null) { + log.warn("[buildPropertyReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject); + params = new JSONObject(); + } + + // 将标准格式的params转换为平台需要的properties格式 + Map properties = new HashMap<>(); + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object valueObj = entry.getValue(); + + // 如果是复杂结构(包含value和time) + if (valueObj instanceof JSONObject valueJson) { + properties.put(key, valueJson.getOrDefault("value", valueObj)); + } else { + properties.put(key, valueObj); + } + } + reportReqDTO.setProperties(properties); + return reportReqDTO; } @@ -268,7 +292,15 @@ public class IotDeviceMqttMessageHandler { reportReqDTO.setProductKey(topicParts[2]); reportReqDTO.setDeviceName(topicParts[3]); reportReqDTO.setIdentifier(topicParts[6]); - reportReqDTO.setParams(jsonObject.getJSONObject("params")); + + // 只使用标准JSON格式处理事件参数 + JSONObject params = jsonObject.getJSONObject("params"); + if (params == null) { + log.warn("[buildEventReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject); + params = new JSONObject(); + } + reportReqDTO.setParams(params); + return reportReqDTO; } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java index 31679fe056..6f1e8a11b8 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java @@ -18,7 +18,11 @@ import java.util.Collections; /** * IoT Emqx Webhook 事件处理的 Vert.x Handler * - * EMQXWebhook + * EMQXWebhook + * + * 注意:该处理器需要返回特定格式:{"result": "success"} 或 {"result": "error"}, + * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。 * * @author haohao */ @@ -54,10 +58,12 @@ public class IotDeviceWebhookVertxHandler implements Handler { } // 返回成功响应 - IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "success")); + // 注意:这里必须返回 {"result": "success"} 格式,以符合 EMQX Webhook 插件的要求 + IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "success")); } catch (Exception e) { log.error("[handle][处理 Webhook 事件异常]", e); - IotPluginCommonUtils.writeJson(routingContext, Collections.singletonMap("result", "error")); + // 注意:这里必须返回 {"result": "error"} 格式,以符合 EMQX Webhook 插件的要求 + IotPluginCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "error")); } } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java index bdb92b6ee7..e4d1a73814 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -15,6 +16,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; @@ -28,6 +30,9 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDeviceEventReportVertxHandler implements Handler { public static final String PATH = "/sys/:productKey/:deviceName/thing/event/:identifier/post"; + private static final String VERSION = "1.0"; + private static final String EVENT_METHOD_PREFIX = "thing.event."; + private static final String EVENT_METHOD_SUFFIX = ".post"; private final IotDeviceUpstreamApi deviceUpstreamApi; @@ -36,40 +41,71 @@ public class IotDeviceEventReportVertxHandler implements Handler public void handle(RoutingContext routingContext) { // 1. 解析参数 IotDeviceEventReportReqDTO reportReqDTO; + String identifier = null; + String requestId = IdUtil.fastSimpleUUID(); try { String productKey = routingContext.pathParam("productKey"); String deviceName = routingContext.pathParam("deviceName"); - String identifier = routingContext.pathParam("identifier"); + identifier = routingContext.pathParam("identifier"); JsonObject body = routingContext.body().asJsonObject(); - String id = ObjUtil.defaultIfBlank(body.getString("id"), IdUtil.fastSimpleUUID()); - Map params = (Map) body.getMap().get("params"); - reportReqDTO = ((IotDeviceEventReportReqDTO) - new IotDeviceEventReportReqDTO().setRequestId(id) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) + requestId = ObjUtil.defaultIfBlank(body.getString("id"), requestId); + + // 按照标准JSON格式处理事件参数 + Map params; + + // 优先使用params字段,符合标准 + if (body.getJsonObject("params") != null) { + params = body.getJsonObject("params").getMap(); + } else { + // 兼容旧格式 + params = new HashMap<>(); + } + + reportReqDTO = ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)) .setIdentifier(identifier).setParams(params); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + // 使用IotStandardResponse实体类返回错误 + String method = identifier != null ? EVENT_METHOD_PREFIX + identifier + EVENT_METHOD_SUFFIX + : "thing.event.unknown.post"; + IotStandardResponse errorResponse = IotStandardResponse.error( + requestId, method, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); return; } try { // 2. 设备上线 - deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) - new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) + deviceUpstreamApi.updateDeviceState( + ((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) .setProductKey(reportReqDTO.getProductKey()).setDeviceName(reportReqDTO.getDeviceName())) - .setState(IotDeviceStateEnum.ONLINE.getState())); + .setState(IotDeviceStateEnum.ONLINE.getState())); - // 3.1 属性上报 + // 3.1 事件上报 CommonResult result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); - // 3.2 返回结果 - IotPluginCommonUtils.writeJson(routingContext, result); + + // 3.2 返回结果 - 使用IotStandardResponse实体类 + String method = EVENT_METHOD_PREFIX + reportReqDTO.getIdentifier() + EVENT_METHOD_SUFFIX; + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reportReqDTO.getRequestId(), method, result.getData()); + } else { + response = IotStandardResponse.error( + reportReqDTO.getRequestId(), method, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { - log.error("[handle][请求参数({}) 时间上报异常]", reportReqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + log.error("[handle][请求参数({}) 事件上报异常]", reportReqDTO, e); + + // 构建错误响应 - 使用IotStandardResponse实体类 + String method = EVENT_METHOD_PREFIX + reportReqDTO.getIdentifier() + EVENT_METHOD_SUFFIX; + IotStandardResponse errorResponse = IotStandardResponse.error( + reportReqDTO.getRequestId(), method, + INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } - } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java index 22fb1be821..be3e0017ad 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; import io.vertx.core.Handler; import io.vertx.core.json.JsonObject; @@ -15,6 +16,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; @@ -31,6 +33,8 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC public class IotDevicePropertyReportVertxHandler implements Handler { public static final String PATH = "/sys/:productKey/:deviceName/thing/event/property/post"; + private static final String VERSION = "1.0"; + private static final String METHOD = "thing.event.property.post"; private final IotDeviceUpstreamApi deviceUpstreamApi; @@ -39,43 +43,89 @@ public class IotDevicePropertyReportVertxHandler implements Handler properties = (Map) body.getMap().get("properties"); - reportReqDTO = ((IotDevicePropertyReportReqDTO) - new IotDevicePropertyReportReqDTO().setRequestId(id) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) + requestId = ObjUtil.defaultIfBlank(body.getString("id"), requestId); + + // 按照标准JSON格式处理属性数据 + Map properties = new HashMap<>(); + + // 优先使用params字段,符合标准 + Map params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap() + : null; + + if (params != null) { + // 将标准格式的params转换为平台需要的properties格式 + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object valueObj = entry.getValue(); + + // 如果是复杂结构(包含value和time) + if (valueObj instanceof Map) { + Map valueMap = (Map) valueObj; + if (valueMap.containsKey("value")) { + properties.put(key, valueMap.get("value")); + } else { + properties.put(key, valueObj); + } + } else { + properties.put(key, valueObj); + } + } + } else { + // 兼容旧格式,直接使用properties字段 + properties = body.getJsonObject("properties") != null ? body.getJsonObject("properties").getMap() + : new HashMap<>(); + } + + reportReqDTO = ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)) .setProperties(properties); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(BAD_REQUEST)); + // 使用IotStandardResponse实体类返回错误 + IotStandardResponse errorResponse = IotStandardResponse.error( + requestId, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); return; } // TODO @芋艿:secret 校验。目前的想法: - // 方案一:请求的时候,带上 secret 参数,然后进行校验,减少请求的频次。不过可能要看下 mqtt 能不能复用! - // 方案二:本地有设备信息的缓存,异步刷新。这样可能 mqtt 的校验,和 http 校验都容易适配。 + // 方案一:请求的时候,带上 secret 参数,然后进行校验,减少请求的频次。不过可能要看下 mqtt 能不能复用! + // 方案二:本地有设备信息的缓存,异步刷新。这样可能 mqtt 的校验,和 http 校验都容易适配。 try { // 2. 设备上线 - deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) - new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) + deviceUpstreamApi.updateDeviceState( + ((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) .setProductKey(reportReqDTO.getProductKey()).setDeviceName(reportReqDTO.getDeviceName())) - .setState(IotDeviceStateEnum.ONLINE.getState())); + .setState(IotDeviceStateEnum.ONLINE.getState())); // 3.1 属性上报 CommonResult result = deviceUpstreamApi.reportDeviceProperty(reportReqDTO); - // 3.2 返回结果 - IotPluginCommonUtils.writeJson(routingContext, result); + + // 3.2 返回结果 - 使用IotStandardResponse实体类 + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(reportReqDTO.getRequestId(), METHOD, result.getData()); + } else { + response = IotStandardResponse.error( + reportReqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 属性上报异常]", reportReqDTO, e); - IotPluginCommonUtils.writeJson(routingContext, CommonResult.error(INTERNAL_SERVER_ERROR)); + + // 构建错误响应 - 使用IotStandardResponse实体类 + IotStandardResponse errorResponse = IotStandardResponse.error( + reportReqDTO.getRequestId(), METHOD, + INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } } - } From d94e5779433ab4c7eaa27ff573f3d4636f7c5d86 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 07:00:39 +0800 Subject: [PATCH 281/309] =?UTF-8?q?=E3=80=90=E4=BE=9D=E8=B5=96=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E3=80=91=E5=85=A8=E5=B1=80=EF=BC=9Atika-core=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=203.1.0=20=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 54252350d6..3234a9f8ba 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -60,7 +60,7 @@ 2.14.5 3.11.1 0.1.55 - 3.1.0 + 3.1.0 2.7.0 3.0.6 4.1.116.Final From 81739186c971fdc508cf5cd115e5be49a26f38dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 15 Mar 2025 17:56:45 +0800 Subject: [PATCH 282/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91IoT=EF=BC=9A=E9=87=8D=E6=9E=84=E4=B8=8A?= =?UTF-8?q?=E8=A1=8C=E8=AF=B7=E6=B1=82=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E5=90=88=E5=B9=B6=E5=B1=9E=E6=80=A7=E5=92=8C=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E4=B8=8A=E6=8A=A5=E5=A4=84=E7=90=86=EF=BC=8C=E7=AE=80?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=EF=BC=8C=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=86=97=E4=BD=99=E5=A4=84=E7=90=86=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 11 -- .../IotKafkaMQDataBridgeExecute.java | 3 +- .../iot/plugin/emqx/config/IotEmqxPlugin.java | 9 +- .../IotDeviceDownstreamHandlerImpl.java | 1 + .../router/IotDeviceMqttMessageHandler.java | 1 + .../upstream/IotDeviceUpstreamServer.java | 13 +- .../IotDeviceEventReportVertxHandler.java | 111 ----------- .../IotDevicePropertyReportVertxHandler.java | 131 ------------- .../router/IotDeviceUpstreamVertxHandler.java | 185 ++++++++++++++++++ 9 files changed, 201 insertions(+), 264 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 1b897c5d7f..d398aa7d81 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -64,17 +64,6 @@ yudao-spring-boot-starter-excel - - - - - - - - - - - org.apache.rocketmq diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 3b7f99bf42..08dfec3338 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -10,7 +10,6 @@ import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.stereotype.Component; import java.time.Duration; import java.time.LocalDateTime; @@ -24,7 +23,7 @@ import java.util.concurrent.TimeUnit; * @author HUIHUI */ @ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") -@Component +//@Component @Slf4j public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute> { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java index af2e568628..74a49c4f19 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java @@ -7,10 +7,13 @@ import org.pf4j.spring.SpringPlugin; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -// TODO @芋艿:完善注释 - /** - * 负责插件的启动和停止 + * EMQX 插件实现类 + * + * 基于 PF4J 插件框架,实现 EMQX 消息中间件的集成 + * 负责插件的生命周期管理,包括启动、停止和应用上下文的创建 + * + * @author 芋道源码 */ @Slf4j public class IotEmqxPlugin extends SpringPlugin { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java index aed677c49e..977f0869c7 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java @@ -27,6 +27,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle private static final String SYS_TOPIC_PREFIX = "/sys/"; // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。 + // 回复 都使用 Alink 格式,方便后续扩展。 // 设备服务调用 标准 JSON // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier}_reply diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java index 4af6877bfd..b92868582c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java @@ -31,6 +31,7 @@ import java.util.Map; public class IotDeviceMqttMessageHandler { // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。 + // 回复 都使用 Alink 格式,方便后续扩展。 // 设备上报属性 标准 JSON // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java index 42da951a24..67129a4d1c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/IotDeviceUpstreamServer.java @@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.upstream; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; import cn.iocoder.yudao.module.iot.plugin.http.config.IotPluginHttpProperties; -import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDeviceEventReportVertxHandler; -import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDevicePropertyReportVertxHandler; +import cn.iocoder.yudao.module.iot.plugin.http.upstream.router.IotDeviceUpstreamVertxHandler; import io.vertx.core.Vertx; import io.vertx.core.http.HttpServer; import io.vertx.ext.web.Router; @@ -32,10 +31,12 @@ public class IotDeviceUpstreamServer { // 创建 Router 实例 Router router = Router.router(vertx); router.route().handler(BodyHandler.create()); // 处理 Body - router.post(IotDevicePropertyReportVertxHandler.PATH) - .handler(new IotDevicePropertyReportVertxHandler(deviceUpstreamApi)); - router.post(IotDeviceEventReportVertxHandler.PATH) - .handler(new IotDeviceEventReportVertxHandler(deviceUpstreamApi)); + + // 使用统一的 Handler 处理所有上行请求 + IotDeviceUpstreamVertxHandler upstreamHandler = new IotDeviceUpstreamVertxHandler(deviceUpstreamApi); + router.post(IotDeviceUpstreamVertxHandler.PROPERTY_PATH).handler(upstreamHandler); + router.post(IotDeviceUpstreamVertxHandler.EVENT_PATH).handler(upstreamHandler); + // 创建 HttpServer 实例 this.server = vertx.createHttpServer().requestHandler(router); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java deleted file mode 100644 index e4d1a73814..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceEventReportVertxHandler.java +++ /dev/null @@ -1,111 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin.http.upstream.router; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; -import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; -import io.vertx.core.Handler; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.RoutingContext; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; - -/** - * IoT 设备事件上报的 Vert.x Handler - */ -@RequiredArgsConstructor -@Slf4j -public class IotDeviceEventReportVertxHandler implements Handler { - - public static final String PATH = "/sys/:productKey/:deviceName/thing/event/:identifier/post"; - private static final String VERSION = "1.0"; - private static final String EVENT_METHOD_PREFIX = "thing.event."; - private static final String EVENT_METHOD_SUFFIX = ".post"; - - private final IotDeviceUpstreamApi deviceUpstreamApi; - - @Override - @SuppressWarnings("unchecked") - public void handle(RoutingContext routingContext) { - // 1. 解析参数 - IotDeviceEventReportReqDTO reportReqDTO; - String identifier = null; - String requestId = IdUtil.fastSimpleUUID(); - try { - String productKey = routingContext.pathParam("productKey"); - String deviceName = routingContext.pathParam("deviceName"); - identifier = routingContext.pathParam("identifier"); - JsonObject body = routingContext.body().asJsonObject(); - requestId = ObjUtil.defaultIfBlank(body.getString("id"), requestId); - - // 按照标准JSON格式处理事件参数 - Map params; - - // 优先使用params字段,符合标准 - if (body.getJsonObject("params") != null) { - params = body.getJsonObject("params").getMap(); - } else { - // 兼容旧格式 - params = new HashMap<>(); - } - - reportReqDTO = ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) - .setIdentifier(identifier).setParams(params); - } catch (Exception e) { - log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - // 使用IotStandardResponse实体类返回错误 - String method = identifier != null ? EVENT_METHOD_PREFIX + identifier + EVENT_METHOD_SUFFIX - : "thing.event.unknown.post"; - IotStandardResponse errorResponse = IotStandardResponse.error( - requestId, method, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); - IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); - return; - } - - try { - // 2. 设备上线 - deviceUpstreamApi.updateDeviceState( - ((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(reportReqDTO.getProductKey()).setDeviceName(reportReqDTO.getDeviceName())) - .setState(IotDeviceStateEnum.ONLINE.getState())); - - // 3.1 事件上报 - CommonResult result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); - - // 3.2 返回结果 - 使用IotStandardResponse实体类 - String method = EVENT_METHOD_PREFIX + reportReqDTO.getIdentifier() + EVENT_METHOD_SUFFIX; - IotStandardResponse response; - if (result.isSuccess()) { - response = IotStandardResponse.success(reportReqDTO.getRequestId(), method, result.getData()); - } else { - response = IotStandardResponse.error( - reportReqDTO.getRequestId(), method, result.getCode(), result.getMsg()); - } - IotPluginCommonUtils.writeJsonResponse(routingContext, response); - } catch (Exception e) { - log.error("[handle][请求参数({}) 事件上报异常]", reportReqDTO, e); - - // 构建错误响应 - 使用IotStandardResponse实体类 - String method = EVENT_METHOD_PREFIX + reportReqDTO.getIdentifier() + EVENT_METHOD_SUFFIX; - IotStandardResponse errorResponse = IotStandardResponse.error( - reportReqDTO.getRequestId(), method, - INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); - IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); - } - } -} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java deleted file mode 100644 index be3e0017ad..0000000000 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDevicePropertyReportVertxHandler.java +++ /dev/null @@ -1,131 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin.http.upstream.router; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; -import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; -import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; -import io.vertx.core.Handler; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.RoutingContext; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; - -// TODO @芋艿:【待定 005】要不要简化成,解析后,统一处理?只有一个 Handler!!! -/** - * IoT 设备属性上报的 Vert.x Handler - * - * @author haohao - */ -@RequiredArgsConstructor -@Slf4j -public class IotDevicePropertyReportVertxHandler implements Handler { - - public static final String PATH = "/sys/:productKey/:deviceName/thing/event/property/post"; - private static final String VERSION = "1.0"; - private static final String METHOD = "thing.event.property.post"; - - private final IotDeviceUpstreamApi deviceUpstreamApi; - - @Override - @SuppressWarnings("unchecked") - public void handle(RoutingContext routingContext) { - // 1. 解析参数 - IotDevicePropertyReportReqDTO reportReqDTO; - String requestId = IdUtil.fastSimpleUUID(); - try { - String productKey = routingContext.pathParam("productKey"); - String deviceName = routingContext.pathParam("deviceName"); - JsonObject body = routingContext.body().asJsonObject(); - requestId = ObjUtil.defaultIfBlank(body.getString("id"), requestId); - - // 按照标准JSON格式处理属性数据 - Map properties = new HashMap<>(); - - // 优先使用params字段,符合标准 - Map params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap() - : null; - - if (params != null) { - // 将标准格式的params转换为平台需要的properties格式 - for (Map.Entry entry : params.entrySet()) { - String key = entry.getKey(); - Object valueObj = entry.getValue(); - - // 如果是复杂结构(包含value和time) - if (valueObj instanceof Map) { - Map valueMap = (Map) valueObj; - if (valueMap.containsKey("value")) { - properties.put(key, valueMap.get("value")); - } else { - properties.put(key, valueObj); - } - } else { - properties.put(key, valueObj); - } - } - } else { - // 兼容旧格式,直接使用properties字段 - properties = body.getJsonObject("properties") != null ? body.getJsonObject("properties").getMap() - : new HashMap<>(); - } - - reportReqDTO = ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) - .setProperties(properties); - } catch (Exception e) { - log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - // 使用IotStandardResponse实体类返回错误 - IotStandardResponse errorResponse = IotStandardResponse.error( - requestId, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); - IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); - return; - } - - // TODO @芋艿:secret 校验。目前的想法: - // 方案一:请求的时候,带上 secret 参数,然后进行校验,减少请求的频次。不过可能要看下 mqtt 能不能复用! - // 方案二:本地有设备信息的缓存,异步刷新。这样可能 mqtt 的校验,和 http 校验都容易适配。 - - try { - // 2. 设备上线 - deviceUpstreamApi.updateDeviceState( - ((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) - .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) - .setProductKey(reportReqDTO.getProductKey()).setDeviceName(reportReqDTO.getDeviceName())) - .setState(IotDeviceStateEnum.ONLINE.getState())); - - // 3.1 属性上报 - CommonResult result = deviceUpstreamApi.reportDeviceProperty(reportReqDTO); - - // 3.2 返回结果 - 使用IotStandardResponse实体类 - IotStandardResponse response; - if (result.isSuccess()) { - response = IotStandardResponse.success(reportReqDTO.getRequestId(), METHOD, result.getData()); - } else { - response = IotStandardResponse.error( - reportReqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); - } - IotPluginCommonUtils.writeJsonResponse(routingContext, response); - } catch (Exception e) { - log.error("[handle][请求参数({}) 属性上报异常]", reportReqDTO, e); - - // 构建错误响应 - 使用IotStandardResponse实体类 - IotStandardResponse errorResponse = IotStandardResponse.error( - reportReqDTO.getRequestId(), METHOD, - INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); - IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); - } - } -} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java new file mode 100644 index 0000000000..ce250f41e1 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java @@ -0,0 +1,185 @@ +package cn.iocoder.yudao.module.iot.plugin.http.upstream.router; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import cn.iocoder.yudao.module.iot.plugin.common.pojo.IotStandardResponse; +import cn.iocoder.yudao.module.iot.plugin.common.util.IotPluginCommonUtils; +import io.vertx.core.Handler; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR; + +/** + * IoT 设备上行统一处理的 Vert.x Handler + *

+ * 统一处理设备属性上报和事件上报的请求 + * + * @author haohao + */ +@RequiredArgsConstructor +@Slf4j +public class IotDeviceUpstreamVertxHandler implements Handler { + + // 属性上报路径 + public static final String PROPERTY_PATH = "/sys/:productKey/:deviceName/thing/event/property/post"; + // 事件上报路径 + public static final String EVENT_PATH = "/sys/:productKey/:deviceName/thing/event/:identifier/post"; + + private static final String PROPERTY_METHOD = "thing.event.property.post"; + private static final String EVENT_METHOD_PREFIX = "thing.event."; + private static final String EVENT_METHOD_SUFFIX = ".post"; + + private final IotDeviceUpstreamApi deviceUpstreamApi; + + @Override + public void handle(RoutingContext routingContext) { + String path = routingContext.request().path(); + String requestId = IdUtil.fastSimpleUUID(); + + try { + // 1. 解析通用参数 + String productKey = routingContext.pathParam("productKey"); + String deviceName = routingContext.pathParam("deviceName"); + JsonObject body = routingContext.body().asJsonObject(); + requestId = ObjUtil.defaultIfBlank(body.getString("id"), requestId); + + // 2. 根据路径模式处理不同类型的请求 + CommonResult result; + String method; + + if (path.matches(".*/thing/event/property/post")) { + // 处理属性上报 + IotDevicePropertyReportReqDTO reportReqDTO = parsePropertyReportRequest(productKey, deviceName, requestId, body); + + // 设备上线 + updateDeviceState(reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); + + // 属性上报 + result = deviceUpstreamApi.reportDeviceProperty(reportReqDTO); + method = PROPERTY_METHOD; + } else if (path.matches(".*/thing/event/.+/post")) { + // 处理事件上报 + String identifier = routingContext.pathParam("identifier"); + IotDeviceEventReportReqDTO reportReqDTO = parseEventReportRequest(productKey, deviceName, identifier, requestId, body); + + // 设备上线 + updateDeviceState(reportReqDTO.getProductKey(), reportReqDTO.getDeviceName()); + + // 事件上报 + result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO); + method = EVENT_METHOD_PREFIX + identifier + EVENT_METHOD_SUFFIX; + } else { + // 不支持的请求路径 + IotStandardResponse errorResponse = IotStandardResponse.error(requestId, "unknown", BAD_REQUEST.getCode(), "不支持的请求路径"); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); + return; + } + + // 3. 返回标准响应 + IotStandardResponse response; + if (result.isSuccess()) { + response = IotStandardResponse.success(requestId, method, result.getData()); + } else { + response = IotStandardResponse.error(requestId, method, result.getCode(), result.getMsg()); + } + IotPluginCommonUtils.writeJsonResponse(routingContext, response); + + } catch (Exception e) { + log.error("[handle][处理上行请求异常] path={}", path, e); + + // 构建错误响应 + String method = path.contains("/property/") ? PROPERTY_METHOD : EVENT_METHOD_PREFIX + (routingContext.pathParams().containsKey("identifier") ? routingContext.pathParam("identifier") : "unknown") + EVENT_METHOD_SUFFIX; + + IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); + IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); + } + } + + /** + * 更新设备状态 + * + * @param productKey 产品Key + * @param deviceName 设备名称 + */ + private void updateDeviceState(String productKey, String deviceName) { + deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState())); + } + + /** + * 解析属性上报请求 + * + * @param productKey 产品Key + * @param deviceName 设备名称 + * @param requestId 请求ID + * @param body 请求体 + * @return 属性上报请求DTO + */ + @SuppressWarnings("unchecked") + private IotDevicePropertyReportReqDTO parsePropertyReportRequest(String productKey, String deviceName, String requestId, JsonObject body) { + + // 按照标准JSON格式处理属性数据 + Map properties = new HashMap<>(); + + // 优先使用params字段,符合标准 + Map params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap() : null; + + if (params != null) { + // 将标准格式的params转换为平台需要的properties格式 + for (Map.Entry entry : params.entrySet()) { + String key = entry.getKey(); + Object valueObj = entry.getValue(); + + // 如果是复杂结构(包含value和time) + if (valueObj instanceof Map) { + Map valueMap = (Map) valueObj; + properties.put(key, valueMap.getOrDefault("value", valueObj)); + } else { + properties.put(key, valueObj); + } + } + } + + return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); + } + + /** + * 解析事件上报请求 + * + * @param productKey 产品Key + * @param deviceName 设备名称 + * @param identifier 事件标识符 + * @param requestId 请求ID + * @param body 请求体 + * @return 事件上报请求DTO + */ + @SuppressWarnings("unchecked") + private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, String requestId, JsonObject body) { + + // 按照标准JSON格式处理事件参数 + Map params; + + // 优先使用params字段,符合标准 + if (body.getJsonObject("params") != null) { + params = body.getJsonObject("params").getMap(); + } else { + // 兼容旧格式 + params = new HashMap<>(); + } + + return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); + } +} \ No newline at end of file From 86e4379e62908d938e9239cc14d69006075a28b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=89=E6=B5=A9=E6=B5=A9?= <1036606149@qq.com> Date: Sat, 15 Mar 2025 18:08:05 +0800 Subject: [PATCH 283/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E3=80=91IoT=EF=BC=9A=E6=96=B0=E5=A2=9E=20TDengine=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml | 3 +-- yudao-module-iot/yudao-module-iot-biz/pom.xml | 6 ++++++ .../rule/action/databridge/IotKafkaMQDataBridgeExecute.java | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index 95ab6f7a4e..d5b1893919 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -63,12 +63,11 @@ opengauss-jdbc true - com.taosdata.jdbc taos-jdbcdriver + true - com.alibaba druid-spring-boot-3-starter diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index d398aa7d81..72e5f67b2f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -107,6 +107,12 @@ 24.1.2 + + + com.taosdata.jdbc + taos-jdbcdriver + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java index 08dfec3338..3b7f99bf42 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotKafkaMQDataBridgeExecute.java @@ -10,6 +10,7 @@ import org.apache.kafka.common.serialization.StringSerializer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Component; import java.time.Duration; import java.time.LocalDateTime; @@ -23,7 +24,7 @@ import java.util.concurrent.TimeUnit; * @author HUIHUI */ @ConditionalOnClass(name = "org.springframework.kafka.core.KafkaTemplate") -//@Component +@Component @Slf4j public class IotKafkaMQDataBridgeExecute extends AbstractCacheableDataBridgeExecute> { From dd0cadd4263140349304e7f7570c06594a0040e0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 18:58:02 +0800 Subject: [PATCH 284/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=E5=85=B3=E9=97=AD=20knife4j=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=EF=BC=8C=E5=AD=98=E5=9C=A8=E5=85=BC=E5=AE=B9=E6=80=A7?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E6=82=B2=E4=BC=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-server/src/main/resources/application.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 8c906e3899..522c0845af 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -48,7 +48,7 @@ springdoc: default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 knife4j: - enable: true + enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 setting: language: zh_cn From 075545c11e5fce49d306b3c9e4384d7cedc4d392 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 20:50:19 +0800 Subject: [PATCH 285/309] =?UTF-8?q?=E3=80=90=E4=BE=9D=E8=B5=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7=E3=80=91weixin-java=20from=204.6.0=20to=204.7.2.B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-dependencies/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index 3234a9f8ba..57af0f5bef 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -53,7 +53,7 @@ 1.6.3 5.8.35 6.0.0-M19 - 4.0.3 + 4.0.3 2.4.1 1.2.83 33.4.0-jre @@ -71,7 +71,7 @@ 1.12.777 2.0.5 1.8.1 - 4.6.0 + 4.7.2.B @@ -471,7 +471,7 @@ com.alibaba easyexcel - ${easyexcel.verion} + ${easyexcel.version} commons-io From 5aacefc00e3b7c4071c018db5e52f2d5a57cec27 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 21:07:34 +0800 Subject: [PATCH 286/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91AI=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E8=BF=9B=E8=A1=8C=E9=97=AE=E7=AD=94=E6=97=B6=EF=BC=8C?= =?UTF-8?q?toolNames=20cannot=20be=20null=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java index becc54ee43..5f307cd645 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.ai.core.util; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; @@ -13,6 +14,7 @@ import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.ai.qianfan.QianFanChatOptions; import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; +import java.util.Collections; import java.util.Set; /** @@ -28,6 +30,7 @@ public class AiUtils { public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, Set toolNames) { + toolNames = ObjUtil.defaultIfNull(toolNames, Collections.emptySet()); // noinspection EnhancedSwitchMigration switch (platform) { case TONG_YI: From a4815b30d07105dd2cf89ae99e325e687c5cfe09 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 21:16:53 +0800 Subject: [PATCH 287/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=E5=95=86=E5=9F=8E=EF=BC=9A=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E5=95=86=E5=9F=8E=E4=B8=AD=E5=B0=86=E4=BC=9A=E5=91=98=E7=AD=89?= =?UTF-8?q?=E7=BA=A7=E5=85=B3=E9=97=AD=EF=BC=8C=E8=BF=98=E7=BB=A7=E7=BB=AD?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E6=8A=98=E6=89=A3=E4=BB=B7=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../calculator/TradeDiscountActivityPriceCalculator.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java index 801c7c0186..bf8651af5e 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDiscountActivityPriceCalculator.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.module.member.api.level.MemberLevelApi; import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO; import cn.iocoder.yudao.module.member.api.user.MemberUserApi; @@ -141,7 +142,9 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato */ public Integer calculateVipPrice(MemberLevelRespDTO level, TradePriceCalculateRespBO.OrderItem orderItem) { - if (level == null || level.getDiscountPercent() == null) { + if (level == null + || CommonStatusEnum.isDisable(level.getStatus()) + || level.getDiscountPercent() == null) { return 0; } Integer newPrice = calculateRatePrice(orderItem.getPayPrice(), level.getDiscountPercent().doubleValue()); From 803da9ed9e6bedb209ac8426fff3ea32fa695d4a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 21:37:18 +0800 Subject: [PATCH 288/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91System=EF=BC=9AcomponentName=20=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E6=A0=A1=E9=AA=8C=EF=BC=8C=E9=81=BF=E5=85=8D=20vue3?= =?UTF-8?q?=20router=20=E5=88=9D=E5=A7=8B=E5=8C=96=E6=9C=89=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/enums/ErrorCodeConstants.java | 1 + .../dal/mysql/permission/MenuMapper.java | 5 +++ .../service/permission/MenuServiceImpl.java | 33 +++++++++++++++++-- .../permission/MenuServiceImplTest.java | 8 ++--- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index a3cc1cefa0..6db0e567c4 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -24,6 +24,7 @@ public interface ErrorCodeConstants { ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在"); ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除"); ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单"); + ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单"); // ========== 角色模块 1-002-002-000 ========== ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在"); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java index 8458faa67a..31eb117d26 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java @@ -28,4 +28,9 @@ public interface MenuMapper extends BaseMapperX { default List selectListByPermission(String permission) { return selectList(MenuDO::getPermission, permission); } + + default MenuDO selectByComponentName(String componentName) { + return selectOne(MenuDO::getComponentName, componentName); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java index 98052eb650..5378108dd1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.permission; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; @@ -53,7 +54,8 @@ public class MenuServiceImpl implements MenuService { // 校验父菜单存在 validateParentMenu(createReqVO.getParentId(), null); // 校验菜单(自己) - validateMenu(createReqVO.getParentId(), createReqVO.getName(), null); + validateMenuName(createReqVO.getParentId(), createReqVO.getName(), null); + validateMenuComponentName(createReqVO.getComponentName(), null); // 插入数据库 MenuDO menu = BeanUtils.toBean(createReqVO, MenuDO.class); @@ -74,7 +76,8 @@ public class MenuServiceImpl implements MenuService { // 校验父菜单存在 validateParentMenu(updateReqVO.getParentId(), updateReqVO.getId()); // 校验菜单(自己) - validateMenu(updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getId()); + validateMenuName(updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getId()); + validateMenuComponentName(updateReqVO.getComponentName(), updateReqVO.getId()); // 更新到数据库 MenuDO updateObj = BeanUtils.toBean(updateReqVO, MenuDO.class); @@ -228,7 +231,7 @@ public class MenuServiceImpl implements MenuService { * @param id 菜单编号 */ @VisibleForTesting - void validateMenu(Long parentId, String name, Long id) { + void validateMenuName(Long parentId, String name, Long id) { MenuDO menu = menuMapper.selectByParentIdAndName(parentId, name); if (menu == null) { return; @@ -242,6 +245,30 @@ public class MenuServiceImpl implements MenuService { } } + /** + * 校验菜单组件名是否合法 + * + * @param componentName 组件名 + * @param id 菜单编号 + */ + @VisibleForTesting + void validateMenuComponentName(String componentName, Long id) { + if (StrUtil.isBlank(componentName)) { + return; + } + MenuDO menu = menuMapper.selectByComponentName(componentName); + if (menu == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的菜单 + if (id == null) { + return; + } + if (!menu.getId().equals(id)) { + throw exception(MENU_COMPONENT_NAME_DUPLICATE); + } + } + /** * 初始化菜单的通用属性。 *

diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java index 27f1efb36c..cc393796cc 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java @@ -275,7 +275,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { } @Test - public void testValidateMenu_success() { + public void testValidateMenu_Name_success() { // mock 父子菜单 MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 @@ -284,11 +284,11 @@ public class MenuServiceImplTest extends BaseDbUnitTest { String otherSonMenuName = randomString(); // 调用,无需断言 - menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId); + menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId); } @Test - public void testValidateMenu_sonMenuNameDuplicate() { + public void testValidateMenu_sonMenuNameNameDuplicate() { // mock 父子菜单 MenuDO sonMenu = createParentAndSonMenu(); // 准备参数 @@ -297,7 +297,7 @@ public class MenuServiceImplTest extends BaseDbUnitTest { String otherSonMenuName = sonMenu.getName(); //相同名称 // 调用,并断言异常 - assertServiceException(() -> menuService.validateMenu(parentId, otherSonMenuName, otherSonMenuId), + assertServiceException(() -> menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId), MENU_NAME_DUPLICATE); } From 2511f2c55bb7b1455c7e783e92866624db5210be Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 22:40:31 +0800 Subject: [PATCH 289/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91BPM=EF=BC=9A=E2=80=9C=E7=BB=93=E6=9D=9F?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E4=B8=8D=E8=83=BD=E6=9C=AA=E7=A9=BA=E2=80=9D?= =?UTF-8?q?=E6=96=87=E6=A1=88=E9=83=A8=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index f96d0cb392..dd470d4999 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -966,7 +966,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); List activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey)); EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); - Assert.notNull(endEvent, "结束节点不能未空"); + Assert.notNull(endEvent, "结束节点不能为空"); runtimeService.createChangeActivityStateBuilder() .processInstanceId(processInstanceId) .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId()) From bd93257a26a1bb49ab859365b1f0cec01e4789bd Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 10:42:10 +0800 Subject: [PATCH 290/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91PAY=EF=BC=9AWxAppPayClient=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E7=BB=93=E6=9E=9C=E4=B8=8D=E8=AF=BBid=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/pay/core/client/impl/weixin/WxAppPayClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java index 396694a75b..ec88aa73a6 100644 --- a/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java +++ b/yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; -import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; @@ -41,7 +41,7 @@ public class WxAppPayClient extends AbstractWxPayClient { // 构建 WxPayUnifiedOrderRequest 对象 WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO); // 执行请求 - WxPayMpOrderResult response = client.createOrder(request); + WxPayAppOrderResult response = client.createOrder(request); // 转换结果 return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response), From 2dc8071faa763f835c9b67e87bf570af38bd000f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 16:31:36 +0800 Subject: [PATCH 291/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E5=85=A8=E5=B1=80=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20selectFirstOne=20=E6=96=B9=E6=B3=95=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E5=AE=B9=E6=98=93=E5=87=BA=E7=8E=B0=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E4=B8=8B=E7=9A=84=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/core/mapper/BaseMapperX.java | 30 +++++++++++++++++-- .../history/ProductBrowseHistoryMapper.java | 5 ++-- .../dal/mysql/social/SocialUserMapper.java | 13 ++++---- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java index 01f2142306..167a0fc4ea 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java @@ -92,10 +92,36 @@ public interface BaseMapperX extends MPJBaseMapper { default T selectOne(SFunction field1, Object value1, SFunction field2, Object value2, SFunction field3, Object value3) { - return selectOne(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2) - .eq(field3, value3)); + return selectOne(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2).eq(field3, value3)); } + /** + * 获取满足条件的第 1 条记录 + * + * 目的:解决并发场景下,插入多条记录后,使用 selectOne 会报错的问题 + * + * @param field 字段名 + * @param value 字段值 + * @return 实体 + */ + default T selectFirstOne(SFunction field, Object value) { + // 如果明确使用 MySQL 等场景,可以考虑使用 LIMIT 1 进行优化 + List list = selectList(new LambdaQueryWrapper().eq(field, value)); + return CollUtil.getFirst(list); + } + + default T selectFirstOne(SFunction field1, Object value1, SFunction field2, Object value2) { + List list = selectList(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2)); + return CollUtil.getFirst(list); + } + + default T selectFirstOne(SFunction field1, Object value1, SFunction field2, Object value2, + SFunction field3, Object value3) { + List list = selectList(new LambdaQueryWrapper().eq(field1, value1).eq(field2, value2).eq(field3, value3)); + return CollUtil.getFirst(list); + } + + default Long selectCount() { return selectCount(new QueryWrapper<>()); } diff --git a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java index 124357cacf..8ea2ca5105 100644 --- a/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java +++ b/yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/history/ProductBrowseHistoryMapper.java @@ -21,9 +21,8 @@ import java.util.Collection; public interface ProductBrowseHistoryMapper extends BaseMapperX { default ProductBrowseHistoryDO selectByUserIdAndSpuId(Long userId, Long spuId) { - return selectOne(new LambdaQueryWrapperX() - .eq(ProductBrowseHistoryDO::getUserId, userId) - .eq(ProductBrowseHistoryDO::getSpuId, spuId)); + return selectFirstOne(ProductBrowseHistoryDO::getUserId, userId, + ProductBrowseHistoryDO::getSpuId, spuId); } default PageResult selectPage(ProductBrowseHistoryPageReqVO reqVO) { diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java index af30ecee29..a90e6ac2a1 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java @@ -5,23 +5,20 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.apache.ibatis.annotations.Mapper; @Mapper public interface SocialUserMapper extends BaseMapperX { default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) { - return selectOne(new LambdaQueryWrapper() - .eq(SocialUserDO::getType, type) - .eq(SocialUserDO::getCode, code) - .eq(SocialUserDO::getState, state)); + return selectOne(SocialUserDO::getType, type, + SocialUserDO::getCode, code, + SocialUserDO::getState, state); } default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) { - return selectOne(new LambdaQueryWrapper() - .eq(SocialUserDO::getType, type) - .eq(SocialUserDO::getOpenid, openid)); + return selectFirstOne(SocialUserDO::getType, type, + SocialUserDO::getOpenid, openid); } default PageResult selectPage(SocialUserPageReqVO reqVO) { From 8ccc55d1aa2759e09e4f76305e84e67c55847f6d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 16:45:48 +0800 Subject: [PATCH 292/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E5=95=86=E5=9F=8E=EF=BC=9AKdNiaoExpressClien?= =?UTF-8?q?t=20=E5=A2=9E=E5=8A=A0=20requestType=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=85=8D=E8=B4=B9=E7=89=88=EF=BC=8C?= =?UTF-8?q?=E8=BF=98=E6=98=AF=E5=A2=9E=E5=80=BC=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../delivery/config/TradeExpressProperties.java | 9 +++++++++ .../core/client/impl/kdniao/KdNiaoExpressClient.java | 7 +------ yudao-server/src/main/resources/application.yaml | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java index 3d836bb176..9cd6ff7938 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/config/TradeExpressProperties.java @@ -56,6 +56,15 @@ public class TradeExpressProperties { @NotEmpty(message = "快递鸟 Api Key 配置项不能为空") private String apiKey; + /** + * 接口指令 + * + * 1. 1002:免费版(只能查询申通、圆通快递) + * 2. 8001:付费版 + */ + @NotEmpty(message = "RequestType 配置项不能为空") + private String requestType = "1002"; + } /** diff --git a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java index 24cf8e6eda..5ecd1c3f01 100644 --- a/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java +++ b/yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/framework/delivery/core/client/impl/kdniao/KdNiaoExpressClient.java @@ -39,11 +39,6 @@ public class KdNiaoExpressClient implements ExpressClient { private static final String REAL_TIME_QUERY_URL = "https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx"; - /** - * 快递鸟即时查询免费版 RequestType - */ - private static final String REAL_TIME_FREE_REQ_TYPE = "1002"; - private final RestTemplate restTemplate; private final TradeExpressProperties.KdNiaoConfig config; @@ -67,7 +62,7 @@ public class KdNiaoExpressClient implements ExpressClient { && StrUtil.length(reqDTO.getPhone()) >= 4) { requestDTO.setCustomerName(StrUtil.subSufByLength(reqDTO.getPhone(), 4)); } - KdNiaoExpressQueryRespDTO respDTO = httpRequest(REAL_TIME_QUERY_URL, REAL_TIME_FREE_REQ_TYPE, + KdNiaoExpressQueryRespDTO respDTO = httpRequest(REAL_TIME_QUERY_URL, config.getRequestType(), requestDTO, KdNiaoExpressQueryRespDTO.class); // 处理结果 diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 522c0845af..838d66b9d4 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -340,10 +340,11 @@ yudao: receive-expire-time: 14d # 收货的过期时间 comment-expire-time: 7d # 评论的过期时间 express: - client: kd_niao + client: KD_NIAO kd-niao: api-key: cb022f1e-48f1-4c4a-a723-9001ac9676b8 business-id: 1809751 + request-type: 1002 # 免费版 1002;付费版 8001 kd100: key: pLXUGAwK5305 customer: E77DF18BE109F454A5CD319E44BF5177 From c95f0c152dd6cca5a9fbe40b8b02d1c14fd6bc17 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 16:58:13 +0800 Subject: [PATCH 293/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E5=85=A8=E5=B1=80=EF=BC=9A=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20methodArgumentNotValidExceptionExceptionHandler=20=E5=AF=B9?= =?UTF-8?q?=E3=80=8C=E7=BB=84=E5=90=88=E6=A0=A1=E9=AA=8C=E3=80=8D=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E7=9A=84=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/handler/GlobalExceptionHandler.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 41c5cead6a..1f19b90186 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.web.core.handler; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjUtil; @@ -27,6 +28,7 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.util.Assert; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; +import org.springframework.validation.ObjectError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; @@ -37,6 +39,7 @@ import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.resource.NoResourceFoundException; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; import java.util.Set; @@ -135,9 +138,23 @@ public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public CommonResult methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) { log.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex); + // 获取 errorMessage + String errorMessage = null; FieldError fieldError = ex.getBindingResult().getFieldError(); - assert fieldError != null; // 断言,避免告警 - return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage())); + if (fieldError == null) { + // 组合校验,参考自 https://t.zsxq.com/3HVTx + List allErrors = ex.getBindingResult().getAllErrors(); + if (CollUtil.isNotEmpty(allErrors)) { + errorMessage = allErrors.get(0).getDefaultMessage(); + } + } else { + errorMessage = fieldError.getDefaultMessage(); + } + // 转换 CommonResult + if (StrUtil.isEmpty(errorMessage)) { + return CommonResult.error(BAD_REQUEST); + } + return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", errorMessage)); } /** From d597d0057ee9d4e5e3ffbc89604bb324c3c5ca7e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 17:04:04 +0800 Subject: [PATCH 294/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=E5=85=A8=E5=B1=80=EF=BC=9Aapplication-dev=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=EF=BC=8C=E5=A4=9A=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=20spring=20=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-server/src/main/resources/application-dev.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml index 9377af95cd..288078968d 100644 --- a/yudao-server/src/main/resources/application-dev.yaml +++ b/yudao-server/src/main/resources/application-dev.yaml @@ -4,11 +4,10 @@ server: --- #################### 数据库相关配置 #################### spring: - spring: - autoconfigure: - exclude: - - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 - - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 + autoconfigure: + exclude: + - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 + - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 # 数据源配置项 datasource: druid: # Druid 【监控】相关的全局配置 From 0bdd000226e83ee7b01794030248522b84375616 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 20:43:11 +0800 Subject: [PATCH 295/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9Amqtt=20=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 11 +++--- .../IotDeviceConfigSetVertxHandler.java | 7 +--- .../IotDeviceOtaUpgradeVertxHandler.java | 7 +--- .../IotDevicePropertyGetVertxHandler.java | 7 +--- .../IotDevicePropertySetVertxHandler.java | 7 +--- .../IotDeviceServiceInvokeVertxHandler.java | 7 +--- .../common/pojo/IotStandardResponse.java | 1 + .../iot/plugin/emqx/config/IotEmqxPlugin.java | 7 ++-- .../IotDeviceDownstreamHandlerImpl.java | 16 ++------ .../router/IotDeviceAuthVertxHandler.java | 9 ++--- .../router/IotDeviceMqttMessageHandler.java | 39 +++++++------------ .../router/IotDeviceWebhookVertxHandler.java | 9 ++--- .../router/IotDeviceUpstreamVertxHandler.java | 34 ++++++++-------- 13 files changed, 61 insertions(+), 100 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index 72e5f67b2f..b9b97cd81f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -42,6 +42,11 @@ + + com.taosdata.jdbc + taos-jdbcdriver + + cn.iocoder.boot yudao-spring-boot-starter-mybatis @@ -107,12 +112,6 @@ 24.1.2 - - - com.taosdata.jdbc - taos-jdbcdriver - - diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java index 6d2b3b5bae..b9bd4a52f1 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java @@ -46,7 +46,6 @@ public class IotDeviceConfigSetVertxHandler implements Handler { .setConfig(config); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); @@ -57,18 +56,16 @@ public class IotDeviceConfigSetVertxHandler implements Handler { try { CommonResult result = deviceDownstreamHandler.setDeviceConfig(reqDTO); - // 使用IotStandardResponse实体类返回结果 + // 3. 响应结果 IotStandardResponse response; if (result.isSuccess()) { response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); } else { - response = IotStandardResponse.error( - reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); } IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 配置设置异常]", reqDTO, e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java index 888677d8b2..a49b84acca 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java @@ -51,7 +51,6 @@ public class IotDeviceOtaUpgradeVertxHandler implements Handler .setInformation(information); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); @@ -62,18 +61,16 @@ public class IotDeviceOtaUpgradeVertxHandler implements Handler try { CommonResult result = deviceDownstreamHandler.upgradeDeviceOta(reqDTO); - // 使用IotStandardResponse实体类返回结果 + // 3. 响应结果 IotStandardResponse response; if (result.isSuccess()) { response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); } else { - response = IotStandardResponse.error( - reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); } IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) OTA 升级异常]", reqDTO, e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java index dc2a8acfef..3cb4bc941d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertyGetVertxHandler.java @@ -46,7 +46,6 @@ public class IotDevicePropertyGetVertxHandler implements Handler .setIdentifiers(identifiers); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); @@ -57,18 +56,16 @@ public class IotDevicePropertyGetVertxHandler implements Handler try { CommonResult result = deviceDownstreamHandler.getDeviceProperty(reqDTO); - // 使用IotStandardResponse实体类返回结果 + // 3. 响应结果 IotStandardResponse response; if (result.isSuccess()) { response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); } else { - response = IotStandardResponse.error( - reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); } IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 属性获取异常]", reqDTO, e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java index 4f0afdccf2..251be1eb9d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDevicePropertySetVertxHandler.java @@ -46,7 +46,6 @@ public class IotDevicePropertySetVertxHandler implements Handler .setProperties(properties); } catch (Exception e) { log.error("[handle][路径参数({}) 解析参数失败]", routingContext.pathParams(), e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( null, METHOD, BAD_REQUEST.getCode(), BAD_REQUEST.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); @@ -57,18 +56,16 @@ public class IotDevicePropertySetVertxHandler implements Handler try { CommonResult result = deviceDownstreamHandler.setDeviceProperty(reqDTO); - // 使用IotStandardResponse实体类返回结果 + // 3. 响应结果 IotStandardResponse response; if (result.isSuccess()) { response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); } else { - response = IotStandardResponse.error( - reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); + response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); } IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 属性设置异常]", reqDTO, e); - // 使用IotStandardResponse实体类返回错误 IotStandardResponse errorResponse = IotStandardResponse.error( reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java index 3a52f212c2..534823f75e 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceServiceInvokeVertxHandler.java @@ -48,7 +48,6 @@ public class IotDeviceServiceInvokeVertxHandler implements Handler result = deviceDownstreamHandler.invokeDeviceService(reqDTO); - // 使用IotStandardResponse实体类返回结果 + // 3. 响应结果 String method = METHOD_PREFIX + reqDTO.getIdentifier() + METHOD_SUFFIX; IotStandardResponse response; if (result.isSuccess()) { response = IotStandardResponse.success(reqDTO.getRequestId(), method, result.getData()); } else { - response = IotStandardResponse.error( - reqDTO.getRequestId(), method, result.getCode(), result.getMsg()); + response = IotStandardResponse.error(reqDTO.getRequestId(), method, result.getCode(), result.getMsg()); } IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 服务调用异常]", reqDTO, e); - // 使用IotStandardResponse实体类返回错误 String method = METHOD_PREFIX + reqDTO.getIdentifier() + METHOD_SUFFIX; IotStandardResponse errorResponse = IotStandardResponse.error( reqDTO.getRequestId(), method, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java index a006f3a6ad..08b6149c33 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.pojo; import lombok.Data; import lombok.experimental.Accessors; +// TODO @芋艿:1)后续考虑,要不要叫 Iot 网关之类的 Response;2)包名 pojo /** * IoT 标准协议响应实体类 *

diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java index 74a49c4f19..275c20eb1c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotEmqxPlugin.java @@ -10,10 +10,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext /** * EMQX 插件实现类 * - * 基于 PF4J 插件框架,实现 EMQX 消息中间件的集成 - * 负责插件的生命周期管理,包括启动、停止和应用上下文的创建 + * 基于 PF4J 插件框架,实现 EMQX 消息中间件的集成:负责插件的生命周期管理,包括启动、停止和应用上下文的创建 * - * @author 芋道源码 + * @author haohao */ @Slf4j public class IotEmqxPlugin extends SpringPlugin { @@ -26,7 +25,6 @@ public class IotEmqxPlugin extends SpringPlugin { public void start() { log.info("[EmqxPlugin][EmqxPlugin 插件启动开始...]"); try { - log.info("[EmqxPlugin][EmqxPlugin 插件启动成功...]"); } catch (Exception e) { log.error("[EmqxPlugin][EmqxPlugin 插件开启动异常...]", e); @@ -52,6 +50,7 @@ public class IotEmqxPlugin extends SpringPlugin { // 继续使用插件自己的 ClassLoader 以加载插件内部的类 pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); // 扫描当前插件的自动配置包 + // TODO @芋艿:是不是要配置下包 pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.emqx.config"); pluginContext.refresh(); return pluginContext; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java index 977f0869c7..c1e64afb97 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java @@ -17,7 +17,6 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.MQTT_TOPIC_IL /** * EMQX 插件的 {@link IotDeviceDownstreamHandler} 实现类 - *

* * @author 芋道源码 */ @@ -26,8 +25,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle private static final String SYS_TOPIC_PREFIX = "/sys/"; - // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。 - // 回复 都使用 Alink 格式,方便后续扩展。 + // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。;回复 都使用 Alink 格式,方便后续扩展。 // 设备服务调用 标准 JSON // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} // 响应Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier}_reply @@ -62,11 +60,8 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle try { // 构建请求主题 String topic = buildServiceTopic(reqDTO.getProductKey(), reqDTO.getDeviceName(), reqDTO.getIdentifier()); - - // 生成请求ID(如果没有提供) - String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); - // 构建请求消息 + String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); JSONObject request = buildServiceRequest(requestId, reqDTO.getIdentifier(), reqDTO.getParams()); // 发送消息 @@ -98,11 +93,8 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle try { // 构建请求主题 String topic = buildPropertySetTopic(reqDTO.getProductKey(), reqDTO.getDeviceName()); - - // 生成请求ID(如果没有提供) - String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); - // 构建请求消息 + String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); JSONObject request = buildPropertySetRequest(requestId, reqDTO.getProperties()); // 发送消息 @@ -163,7 +155,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle } /** - * 发布MQTT消息 + * 发布 MQTT 消息 */ private void publishMessage(String topic, JSONObject payload) { mqttClient.publish( diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java index 472eb83f7f..fcb2286158 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -13,13 +13,12 @@ import lombok.extern.slf4j.Slf4j; import java.util.Collections; /** - * IoT Emqx 连接认证的 Vert.x Handler - * MQTT - * HTTP + * IoT EMQX 连接认证的 Vert.x Handler + * + * EMQX HTTP * * 注意:该处理器需要返回特定格式:{"result": "allow"} 或 {"result": "deny"}, - * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类。 + * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类 * * @author haohao */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java index b92868582c..6cf8d84c5c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.emqx.upstream.router; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -21,26 +22,20 @@ import java.util.Map; /** * IoT 设备 MQTT 消息处理器 - *

- * 参考: - *

- * "..."> + * + * 参考:"设备属性、事件、服务"> */ @Slf4j public class IotDeviceMqttMessageHandler { - // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。 - // 回复 都使用 Alink 格式,方便后续扩展。 + // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈;回复 都使用 Alink 格式,方便后续扩展。 // 设备上报属性 标准 JSON // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply // 设备上报事件 标准 JSON - // 请求 - // Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post - // 响应 - // Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply + // 请求 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post + // 响应 Topic:/sys/${productKey}/${deviceName}/thing/event/${tsl.event.identifier}/post_reply private static final String SYS_TOPIC_PREFIX = "/sys/"; private static final String PROPERTY_POST_TOPIC = "/thing/event/property/post"; @@ -70,7 +65,7 @@ public class IotDeviceMqttMessageHandler { log.info("[messageHandler][接收到消息][topic: {}][payload: {}]", topic, payload); try { - if (payload == null || payload.isEmpty()) { + if (StrUtil.isEmpty(payload)) { log.warn("[messageHandler][消息内容为空][topic: {}]", topic); return; } @@ -214,27 +209,20 @@ public class IotDeviceMqttMessageHandler { * @param topic 原始主题 * @param jsonObject 原始消息JSON对象 * @param method 响应方法 - * @param customData 自定义数据,可为null + * @param customData 自定义数据,可为 null */ private void sendResponse(String topic, JSONObject jsonObject, String method, Object customData) { String replyTopic = topic + REPLY_SUFFIX; - // 使用IotStandardResponse实体类构建响应 + // 响应结果 IotStandardResponse response = IotStandardResponse.success( - jsonObject.getStr("id"), - method, - customData); - + jsonObject.getStr("id"), method, customData); try { - mqttClient.publish(replyTopic, - Buffer.buffer(JsonUtils.toJsonString(response)), - MqttQoS.AT_LEAST_ONCE, - false, - false); + mqttClient.publish(replyTopic, Buffer.buffer(JsonUtils.toJsonString(response)), + MqttQoS.AT_LEAST_ONCE, false, false); log.info("[sendResponse][发送响应消息成功][topic: {}]", replyTopic); } catch (Exception e) { - log.error("[sendResponse][发送响应消息失败][topic: {}][response: {}]", - replyTopic, response, e); + log.error("[sendResponse][发送响应消息失败][topic: {}][response: {}]", replyTopic, response, e); } } @@ -304,4 +292,5 @@ public class IotDeviceMqttMessageHandler { return reportReqDTO; } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java index 6f1e8a11b8..93fb01bc0a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java @@ -16,13 +16,12 @@ import java.time.LocalDateTime; import java.util.Collections; /** - * IoT Emqx Webhook 事件处理的 Vert.x Handler + * IoT EMQX Webhook 事件处理的 Vert.x Handler * - * EMQXWebhook + * EMQX Webhook * * 注意:该处理器需要返回特定格式:{"result": "success"} 或 {"result": "error"}, - * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。 + * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。 * * @author haohao */ @@ -137,7 +136,7 @@ public class IotDeviceWebhookVertxHandler implements Handler { * 解析用户名,格式为 deviceName&productKey * * @param username 用户名 - * @return 解析结果,[0] 为 deviceName,[1] 为productKey,解析失败返回 null + * @return 解析结果,[0] 为 deviceName,[1] 为 productKey,解析失败返回 null */ private String[] parseUsername(String username) { if (StrUtil.isEmpty(username)) { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java index ce250f41e1..f6c7cc3a27 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java @@ -34,9 +34,13 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC @Slf4j public class IotDeviceUpstreamVertxHandler implements Handler { - // 属性上报路径 + /** + * 属性上报路径 + */ public static final String PROPERTY_PATH = "/sys/:productKey/:deviceName/thing/event/property/post"; - // 事件上报路径 + /** + * 事件上报路径 + */ public static final String EVENT_PATH = "/sys/:productKey/:deviceName/thing/event/:identifier/post"; private static final String PROPERTY_METHOD = "thing.event.property.post"; @@ -60,7 +64,6 @@ public class IotDeviceUpstreamVertxHandler implements Handler { // 2. 根据路径模式处理不同类型的请求 CommonResult result; String method; - if (path.matches(".*/thing/event/property/post")) { // 处理属性上报 IotDevicePropertyReportReqDTO reportReqDTO = parsePropertyReportRequest(productKey, deviceName, requestId, body); @@ -97,13 +100,13 @@ public class IotDeviceUpstreamVertxHandler implements Handler { response = IotStandardResponse.error(requestId, method, result.getCode(), result.getMsg()); } IotPluginCommonUtils.writeJsonResponse(routingContext, response); - } catch (Exception e) { log.error("[handle][处理上行请求异常] path={}", path, e); - // 构建错误响应 - String method = path.contains("/property/") ? PROPERTY_METHOD : EVENT_METHOD_PREFIX + (routingContext.pathParams().containsKey("identifier") ? routingContext.pathParam("identifier") : "unknown") + EVENT_METHOD_SUFFIX; - + String method = path.contains("/property/") ? PROPERTY_METHOD + : EVENT_METHOD_PREFIX + (routingContext.pathParams().containsKey("identifier") + ? routingContext.pathParam("identifier") + : "unknown") + EVENT_METHOD_SUFFIX; IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); } @@ -130,20 +133,16 @@ public class IotDeviceUpstreamVertxHandler implements Handler { */ @SuppressWarnings("unchecked") private IotDevicePropertyReportReqDTO parsePropertyReportRequest(String productKey, String deviceName, String requestId, JsonObject body) { - - // 按照标准JSON格式处理属性数据 + // 按照标准 JSON 格式处理属性数据 Map properties = new HashMap<>(); - - // 优先使用params字段,符合标准 + // 优先使用 params 字段,符合标准 Map params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap() : null; - if (params != null) { - // 将标准格式的params转换为平台需要的properties格式 + // 将标准格式的 params 转换为平台需要的 properties 格式 for (Map.Entry entry : params.entrySet()) { String key = entry.getKey(); Object valueObj = entry.getValue(); - - // 如果是复杂结构(包含value和time) + // 如果是复杂结构(包含 value 和 time) if (valueObj instanceof Map) { Map valueMap = (Map) valueObj; properties.put(key, valueMap.getOrDefault("value", valueObj)); @@ -153,6 +152,7 @@ public class IotDeviceUpstreamVertxHandler implements Handler { } } + // 构建属性上报请求 DTO return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); } @@ -166,12 +166,9 @@ public class IotDeviceUpstreamVertxHandler implements Handler { * @param body 请求体 * @return 事件上报请求DTO */ - @SuppressWarnings("unchecked") private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, String requestId, JsonObject body) { - // 按照标准JSON格式处理事件参数 Map params; - // 优先使用params字段,符合标准 if (body.getJsonObject("params") != null) { params = body.getJsonObject("params").getMap(); @@ -180,6 +177,7 @@ public class IotDeviceUpstreamVertxHandler implements Handler { params = new HashMap<>(); } + // 构建事件上报请求 DTO return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); } } \ No newline at end of file From 3b85adc754d5817d138daf2fc00f99e4938a124a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 20:51:00 +0800 Subject: [PATCH 296/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B0=E6=8D=AE=E6=A1=A5?= =?UTF-8?q?=E6=A2=81=E7=9A=84=E6=8E=A5=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-module-iot/yudao-module-iot-biz/pom.xml | 15 +++++++++++++ .../config/IotDataBridgeAbstractConfig.java | 5 ++++- .../databridge/IotDataBridgeExecuteTest.java | 21 +++++++------------ yudao-server/pom.xml | 13 ------------ 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/pom.xml b/yudao-module-iot/yudao-module-iot-biz/pom.xml index b9b97cd81f..8721e4de93 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -112,6 +112,21 @@ 24.1.2 + + + + + + + + + + + + + + + diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java index 550550d195..16481193f2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; +import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; @@ -7,7 +8,7 @@ import lombok.Data; /** * 抽象类 IotDataBridgeConfig * - * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类。 + * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 * * @author HUIHUI @@ -26,6 +27,8 @@ public abstract class IotDataBridgeAbstractConfig { /** * 配置类型 + * + * 枚举 {@link IotDataBridgeTypeEnum#getType()} */ private String type; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java index 03ea33d682..38586afdd7 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/action/databridge/IotDataBridgeExecuteTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Mockito; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -20,6 +19,7 @@ import java.time.LocalDateTime; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; /** * {@link IotDataBridgeExecute} 实现类的测试 @@ -41,20 +41,14 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { @BeforeEach public void setUp() { // 创建共享的测试消息 - message = IotDeviceMessage.builder() - .requestId("TEST-001") - .productKey("testProduct") - .deviceName("testDevice") - .deviceKey("testDeviceKey") - .type("property") - .identifier("temperature") - .data("{\"value\": 60}") - .reportTime(LocalDateTime.now()) - .tenantId(1L) + message = IotDeviceMessage.builder().requestId("TEST-001").reportTime(LocalDateTime.now()).tenantId(1L) + .productKey("testProduct").deviceName("testDevice").deviceKey("testDeviceKey") + .type("property").identifier("temperature").data("{\"value\": 60}") .build(); // 配置 RestTemplate mock 返回成功响应 - Mockito.when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class))) + // TODO @puhui999:这个应该放到 testHttpDataBridge 里 + when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class))) .thenReturn(new ResponseEntity<>("Success", HttpStatus.OK)); } @@ -64,6 +58,7 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { IotKafkaMQDataBridgeExecute action = new IotKafkaMQDataBridgeExecute(); // 2. 创建配置 + // TODO @puhui999:可以改成链式哈。 IotDataBridgeKafkaMQConfig config = new IotDataBridgeKafkaMQConfig(); config.setBootstrapServers("127.0.0.1:9092"); config.setTopic("test-topic"); @@ -156,4 +151,4 @@ public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest { httpDataBridgeExecute.execute(message, new IotDataBridgeDO().setType(httpDataBridgeExecute.getType()).setConfig(config)); } -} +} diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 0251e7b649..17403fef85 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -114,19 +114,6 @@ yudao-module-iot-biz ${revision} - - - - - - - - - - - - - From 44d7d623b33514d7391ce1a3bc8a99cc3bf18425 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 21:26:51 +0800 Subject: [PATCH 297/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AOTA=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../firmware/IotOtaFirmwareCreateReqVO.java | 1 + .../firmware/IotOtaFirmwareUpdateReqVO.java | 3 +- .../record/IotOtaUpgradeRecordPageReqVO.java | 5 ++-- .../task/IotOtaUpgradeTaskSaveReqVO.java | 5 +++- .../ota/IotOtaUpgradeRecordConvert.java | 11 ------- .../dal/dataobject/ota/IotOtaFirmwareDO.java | 1 + .../dal/mysql/ota/IotOtaFirmwareMapper.java | 6 +--- .../mysql/ota/IotOtaUpgradeRecordMapper.java | 15 ++++++---- .../mysql/plugin/IotPluginInstanceMapper.java | 1 + .../service/ota/IotOtaFirmwareService.java | 11 +++---- .../ota/IotOtaFirmwareServiceImpl.java | 4 ++- .../ota/IotOtaUpgradeRecordService.java | 14 +++++---- .../ota/IotOtaUpgradeRecordServiceImpl.java | 30 ++++++++++++------- .../ota/IotOtaUpgradeTaskServiceImpl.java | 18 +++++++---- .../mapper/ota/IotOtaUpgradeRecordMapper.xml | 7 ----- 15 files changed, 73 insertions(+), 59 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java delete mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java index 98b99351de..20cd19536d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java @@ -27,6 +27,7 @@ public class IotOtaFirmwareCreateReqVO { private String productId; @Schema(description = "签名方式", example = "MD5") + // TODO @li:是不是必传哈 private String signMethod; @Schema(description = "固件文件 URL", requiredMode = REQUIRED, example = "https://www.iocoder.cn/yudao-firmware.zip") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java index 88b7cde5cb..4a304338d8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java @@ -7,7 +7,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -@Schema(description = "管理后台 - OTA固件更新 Request VO") +@Schema(description = "管理后台 - OTA 固件更新 Request VO") @Data public class IotOtaFirmwareUpdateReqVO { @@ -15,6 +15,7 @@ public class IotOtaFirmwareUpdateReqVO { @NotNull(message = "固件编号不能为空") private Long id; + // TODO @li:name 是不是可以飞必传哈 @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件") @NotEmpty(message = "固件名称不能为空") private String name; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java index e28024085f..57335ddcbe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java @@ -8,16 +8,17 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA升级记录分页 Request VO") +@Schema(description = "管理后台 - OTA 升级记录分页 Request VO") public class IotOtaUpgradeRecordPageReqVO extends PageParam { + // TODO @li:已经有注解,不用重复注释 /** * 升级任务编号字段。 *

* 该字段用于标识升级任务的唯一编号,不能为空。 */ - @NotNull(message = "升级任务编号不能为空") @Schema(description = "升级任务编号", requiredMode = REQUIRED, example = "1024") + @NotNull(message = "升级任务编号不能为空") private Long taskId; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java index e2fbd8efe6..687cf2b255 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java @@ -14,9 +14,12 @@ import java.util.List; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA升级任务创建/修改 Request VO") +@Schema(description = "管理后台 - OTA 升级任务创建/修改 Request VO") public class IotOtaUpgradeTaskSaveReqVO { + // TODO @li:已经有注解,不用重复注释 + // TODO @li: @Schema 写在参数校验前面。先有定义;其他的,也检查下; + /** * 任务名称 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java deleted file mode 100644 index fe5279b3c6..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/ota/IotOtaUpgradeRecordConvert.java +++ /dev/null @@ -1,11 +0,0 @@ -package cn.iocoder.yudao.module.iot.convert.ota; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -@Mapper -public interface IotOtaUpgradeRecordConvert { - - IotOtaUpgradeRecordConvert INSTANCE = Mappers.getMapper(IotOtaUpgradeRecordConvert.class); - -} diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java index 12e5147edd..af5d8ac359 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java @@ -46,6 +46,7 @@ public class IotOtaFirmwareDO extends BaseDO { * * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()} */ + // TODO @li:帮我改成 Long 哈,写错了 private String productId; /** * 产品标识 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java index 9652942b22..7adf79349b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaFirmwareMapper.java @@ -9,11 +9,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; -/** - * OTA固件 Mapper - * - * @author Shelly - */ +// TODO @li:参考 IotOtaUpgradeRecordMapper 的写法 @Mapper public interface IotOtaFirmwareMapper extends BaseMapperX { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index 46ac870849..8591e1539c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -13,14 +13,10 @@ import org.apache.ibatis.annotations.Select; import java.util.List; import java.util.Map; -/** - * OTA 升级记录 Mapper - * - * @author Shelly - */ @Mapper public interface IotOtaUpgradeRecordMapper extends BaseMapperX { + // TODO @li:selectByFirmwareIdAndTaskIdAndDeviceId;让方法自解释 /** * 根据条件查询单个OTA升级记录 * @@ -37,6 +33,7 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX> selectOtaUpgradeRecordStatistics(Long firmwareId); + // TODO @li:这里的注释,可以去掉哈 /** * 根据分页查询条件获取IOT OTA升级记录的分页结果 * * @param pageReqVO 分页查询请求参数,包含设备名称、任务ID等查询条件 * @return 返回分页查询结果,包含符合条件的IOT OTA升级记录列表 */ + // TODO @li:selectPage 就 ok 拉。 default PageResult selectUpgradeRecordPage(IotOtaUpgradeRecordPageReqVO pageReqVO) { + // TODO @li:这里的注释,可以去掉哈;然后下面的“如果”。。。也没必要注释 // 使用LambdaQueryWrapperX构建查询条件,并根据请求参数动态添加查询条件 return selectPage(pageReqVO, new LambdaQueryWrapperX() .likeIfPresent(IotOtaUpgradeRecordDO::getDeviceName, pageReqVO.getDeviceName()) // 如果设备名称存在,则添加模糊查询条件 .eqIfPresent(IotOtaUpgradeRecordDO::getTaskId, pageReqVO.getTaskId())); // 如果任务ID存在,则添加等值查询条件 } + // TODO @li:这里的注释,可以去掉哈 /** * 根据任务ID和状态更新升级记录的状态 *

@@ -97,6 +98,7 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX() @@ -106,6 +108,7 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX @@ -120,6 +123,7 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX @@ -137,6 +141,7 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInstanceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInstanceMapper.java index 9bd697dde9..93ffe87283 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInstanceMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/IotPluginInstanceMapper.java @@ -8,6 +8,7 @@ import org.apache.ibatis.annotations.Mapper; import java.time.LocalDateTime; import java.util.List; +// TODO @li:参考 IotOtaUpgradeRecordMapper 的写法 @Mapper public interface IotPluginInstanceMapper extends BaseMapperX { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java index 124c803b39..99e3b382a5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareService.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware.IotOtaFirmwa import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO; import jakarta.validation.Valid; +// TODO @li:注释写的有点冗余,可以看看别的模块哈。= = AI 生成的注释,有的时候太啰嗦了,需要处理下的哈 /** * OTA 固件管理 Service * @@ -15,7 +16,7 @@ import jakarta.validation.Valid; public interface IotOtaFirmwareService { /** - * 创建OTA固件 + * 创建 OTA 固件 * * @param saveReqVO OTA固件保存请求对象,包含固件的相关信息 * @return 返回新创建的固件的ID @@ -23,14 +24,14 @@ public interface IotOtaFirmwareService { Long createOtaFirmware(@Valid IotOtaFirmwareCreateReqVO saveReqVO); /** - * 更新OTA固件信息 + * 更新 OTA 固件信息 * * @param updateReqVO OTA固件保存请求对象,包含需要更新的固件信息 */ void updateOtaFirmware(@Valid IotOtaFirmwareUpdateReqVO updateReqVO); /** - * 根据ID获取OTA固件信息 + * 根据 ID 获取 OTA 固件信息 * * @param id OTA固件的唯一标识符 * @return 返回OTA固件的详细信息对象 @@ -38,7 +39,7 @@ public interface IotOtaFirmwareService { IotOtaFirmwareDO getOtaFirmware(Long id); /** - * 分页查询OTA固件信息 + * 分页查询 OTA 固件信息 * * @param pageReqVO 包含分页查询条件的请求对象 * @return 返回分页查询结果,包含固件信息列表和分页信息 @@ -46,7 +47,7 @@ public interface IotOtaFirmwareService { PageResult getOtaFirmwarePage(@Valid IotOtaFirmwarePageReqVO pageReqVO); /** - * 验证物联网OTA固件是否存在 + * 验证物联网 OTA 固件是否存在 * * @param id 固件的唯一标识符 * 该方法用于检查系统中是否存在与给定ID关联的物联网OTA固件信息 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java index d66ce0e4f6..7c0ddba7cf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaFirmwareServiceImpl.java @@ -39,13 +39,14 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { public Long createOtaFirmware(IotOtaFirmwareCreateReqVO saveReqVO) { // 1. 校验固件产品 + 版本号不能重复 validateProductAndVersionDuplicate(saveReqVO.getProductId(), saveReqVO.getVersion()); + // 2.1.转化数据格式,准备存储到数据库中 IotOtaFirmwareDO firmware = BeanUtils.toBean(saveReqVO, IotOtaFirmwareDO.class); // 2.2.查询ProductKey + // TODO @li:productService.getProduct(Convert.toLong(firmware.getProductId())) 放到 1. 后面,先做参考校验。逻辑两段:1)先参数校验;2)构建对象 + 存储 IotProductDO product = productService.getProduct(Convert.toLong(firmware.getProductId())); firmware.setProductKey(Objects.requireNonNull(product).getProductKey()); // TODO @芋艿: 附件、附件签名等属性的计算 - otaFirmwareMapper.insert(firmware); return firmware.getId(); } @@ -79,6 +80,7 @@ public class IotOtaFirmwareServiceImpl implements IotOtaFirmwareService { return firmware; } + // TODO @li:注释有点冗余 /** * 验证产品和版本号是否重复 *

diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java index 86bc18b845..cbf900ac0a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordService.java @@ -8,6 +8,7 @@ import jakarta.validation.Valid; import java.util.List; import java.util.Map; +// TODO @li:注释写的有点冗余,可以看看别的模块哈。= = AI 生成的注释,有的时候太啰嗦了,需要处理下的哈 /** * IotOtaUpgradeRecordService 接口定义了与物联网设备OTA升级记录相关的操作。 * 该接口提供了创建、更新、查询、统计和重试升级记录的功能。 @@ -15,7 +16,7 @@ import java.util.Map; public interface IotOtaUpgradeRecordService { /** - * 批量创建OTA升级记录。 + * 批量创建 OTA 升级记录 * 该函数用于为指定的设备列表、固件ID和升级任务ID创建OTA升级记录。 * * @param deviceIds 设备ID列表,表示需要升级的设备集合。 @@ -25,7 +26,7 @@ public interface IotOtaUpgradeRecordService { void createOtaUpgradeRecordBatch(List deviceIds, Long firmwareId, Long upgradeTaskId); /** - * 获取OTA升级记录的数量统计。 + * 获取 OTA 升级记录的数量统计 * * @return 返回一个 Map,其中键为状态码,值为对应状态的升级记录数量 */ @@ -39,14 +40,14 @@ public interface IotOtaUpgradeRecordService { Map getOtaUpgradeRecordStatistics(Long firmwareId); /** - * 重试指定的OTA升级记录。 + * 重试指定的 OTA 升级记录 * * @param id 需要重试的升级记录的ID。 */ void retryUpgradeRecord(Long id); /** - * 获取指定ID的OTA升级记录的详细信息。 + * 获取指定 ID 的 OTA 升级记录的详细信息。 * * @param id 需要查询的升级记录的ID。 * @return 返回包含升级记录详细信息的响应对象。 @@ -54,7 +55,7 @@ public interface IotOtaUpgradeRecordService { IotOtaUpgradeRecordDO getUpgradeRecord(Long id); /** - * 分页查询OTA升级记录。 + * 分页查询 OTA 升级记录。 * * @param pageReqVO 包含分页查询条件的请求对象,必须经过验证。 * @return 返回包含分页查询结果的响应对象。 @@ -62,7 +63,7 @@ public interface IotOtaUpgradeRecordService { PageResult getUpgradeRecordPage(@Valid IotOtaUpgradeRecordPageReqVO pageReqVO); /** - * 根据任务ID取消升级记录。 + * 根据任务 ID 取消升级记录 *

* 该函数用于根据给定的任务ID,取消与该任务相关的升级记录。通常用于在任务执行失败或用户手动取消时, * 清理或标记相关的升级记录为取消状态。 @@ -71,6 +72,7 @@ public interface IotOtaUpgradeRecordService { */ void cancelUpgradeRecordByTaskId(Long taskId); + // TODO @li:不要的方法,可以删除下哈。 /** * 根据升级状态获取升级记录列表 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java index c04750e004..02ef39cdf1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeRecordServiceImpl.java @@ -25,8 +25,8 @@ import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum.CANCELED; +// TODO @li:@Service、@Validated、@Slf4j,先用关键注解;2)类注释,简单写 @Slf4j @Service @Validated @@ -34,6 +34,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Resource private IotOtaUpgradeRecordMapper upgradeRecordMapper; + // TODO @li:1)@Resource 写在 @Lazy 之前,先用关键注解;2)有必要的情况下,在写 @Lazy 注解。 @Lazy @Resource private IotDeviceService deviceService; @@ -46,8 +47,10 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Override public void createOtaUpgradeRecordBatch(List deviceIds, Long firmwareId, Long upgradeTaskId) { - // 1.校验升级记录信息是否存在,并且已经取消的任务可以重新开始 + // 1. 校验升级记录信息是否存在,并且已经取消的任务可以重新开始 + // TODO @li:批量查询。。 deviceIds.forEach(deviceId -> validateUpgradeRecordDuplicate(firmwareId, upgradeTaskId, String.valueOf(deviceId))); + // 2.初始化OTA升级记录列表信息 IotOtaUpgradeTaskDO upgradeTask = upgradeTaskService.getUpgradeTask(upgradeTaskId); IotOtaFirmwareDO firmware = firmwareService.getOtaFirmware(firmwareId); @@ -70,6 +73,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic } + // TODO @li:1)方法注释,简单写;2)父类写了注释,子类就不用写了。。。 /** * 获取OTA升级记录的数量统计。 * 该方法根据传入的查询条件,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。 @@ -87,10 +91,10 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic Objects.requireNonNull(upgradeRecordCountMap); return upgradeRecordCountMap.entrySet().stream().collect(Collectors.toMap( entry -> Convert.toInt(entry.getKey()), - entry -> Convert.toLong(entry.getValue()) - )); + entry -> Convert.toLong(entry.getValue()))); } + // TODO @li:1)方法注释,简单写;2)父类写了注释,子类就不用写了。。。 /** * 获取指定固件ID的OTA升级记录统计信息。 * 该方法通过查询数据库,统计不同状态的OTA升级记录数量,并返回一个包含各状态数量的映射。 @@ -107,8 +111,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic Objects.requireNonNull(upgradeRecordStatisticsMap); return upgradeRecordStatisticsMap.entrySet().stream().collect(Collectors.toMap( entry -> Convert.toInt(entry.getKey()), - entry -> Convert.toLong(entry.getValue()) - )); + entry -> Convert.toLong(entry.getValue()))); } @Override @@ -118,7 +121,8 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic // 1.2.校验升级记录是否可以重新升级 validateUpgradeRecordCanRetry(upgradeRecord); - // 2.将一些数据重置,这样定时任务轮询就可以重启任务 + // 2. 将一些数据重置,这样定时任务轮询就可以重启任务 + // TODO @li:更新的时候,wherestatus; upgradeRecordMapper.updateById(new IotOtaUpgradeRecordDO() .setId(upgradeRecord.getId()).setProgress(0) .setStatus(IotOtaUpgradeRecordStatusEnum.PENDING.getStatus())); @@ -136,9 +140,9 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic @Override public void cancelUpgradeRecordByTaskId(Long taskId) { - // 暂定只有待推送的升级记录可以取消 + // 暂定只有待推送的升级记录可以取消 TODO @芋艿:可以看看阿里云,哪些可以取消 upgradeRecordMapper.updateUpgradeRecordStatusByTaskIdAndStatus( - CANCELED.getStatus(), taskId, + IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus(), taskId, IotOtaUpgradeRecordStatusEnum.PENDING.getStatus()); } @@ -175,6 +179,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic return upgradeRecord; } + // TODO @li:注释有点冗余 /** * 校验固件升级记录是否重复。 *

@@ -189,13 +194,17 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic // 根据条件查询升级记录 IotOtaUpgradeRecordDO upgradeRecord = upgradeRecordMapper.selectByConditions(firmwareId, taskId, deviceId); // 如果查询到升级记录且状态不是已取消,则抛出异常 + // TODO @li:if return,减少括号层级; + // TODO @li:ObjUtil.notEquals,尽量不用 !取否逻辑; if (upgradeRecord != null) { - if (!CANCELED.getStatus().equals(upgradeRecord.getStatus())) { + if (!IotOtaUpgradeRecordStatusEnum.CANCELED.getStatus().equals(upgradeRecord.getStatus())) { + // TODO @li:提示的时候,需要把 deviceName 给提示出来,不然用户不知道哪个重复啦。 throw exception(OTA_UPGRADE_RECORD_DUPLICATE); } } } + // TODO @li:注释有点冗余 /** * 验证升级记录是否可以重试。 *

@@ -205,6 +214,7 @@ public class IotOtaUpgradeRecordServiceImpl implements IotOtaUpgradeRecordServic * @param upgradeRecord 需要验证的升级记录对象,类型为 IotOtaUpgradeRecordDO * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,则抛出 OTA_UPGRADE_RECORD_CANNOT_RETRY 异常 */ + // TODO @li:这种一次性的方法(不复用的),其实一步一定要抽成小方法; private void validateUpgradeRecordCanRetry(IotOtaUpgradeRecordDO upgradeRecord) { // 检查升级记录的状态是否为 PENDING、PUSHED 或 UPGRADING if (ObjectUtils.equalsAny(upgradeRecord.getStatus(), diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java index ca74481ff2..cee3ba516b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/ota/IotOtaUpgradeTaskServiceImpl.java @@ -27,6 +27,7 @@ import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; +// TODO @li:完善注释、注解顺序 @Slf4j @Service @Validated @@ -54,9 +55,11 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { IotOtaFirmwareDO firmware = firmwareService.validateFirmwareExists(createReqVO.getFirmwareId()); // 1.3 补全设备范围信息,并且校验是否又设备可以升级,如果没有设备可以升级,则报错 validateScopeAndDevice(createReqVO.getScope(), createReqVO.getDeviceIds(), firmware.getProductId()); + // 2. 保存 OTA 升级任务信息到数据库 IotOtaUpgradeTaskDO upgradeTask = initOtaUpgradeTask(createReqVO, firmware.getProductId()); upgradeTaskMapper.insert(upgradeTask); + // 3. 生成设备升级记录信息并存储,等待定时任务轮询 upgradeRecordService.createOtaUpgradeRecordBatch(upgradeTask.getDeviceIds(), firmware.getId(), upgradeTask.getId()); return upgradeTask.getId(); @@ -68,15 +71,16 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { // 1.1 校验升级任务是否存在 IotOtaUpgradeTaskDO upgradeTask = validateUpgradeTaskExists(id); // 1.2 校验升级任务是否可以取消 - // 检查升级任务的状态是否为进行中,只有此状态下的任务才允许取消 + // TODO @li:ObjUtil notequals if (!Objects.equals(upgradeTask.getStatus(), IotOtaUpgradeTaskStatusEnum.IN_PROGRESS.getStatus())) { - // 只有进行中的任务才可以取消 throw exception(OTA_UPGRADE_TASK_CANNOT_CANCEL); } + // 2. 更新 OTA 升级任务状态为已取消 upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder() .id(id).status(IotOtaUpgradeTaskStatusEnum.CANCELED.getStatus()) .build()); + // 3. 更新 OTA 升级记录状态为已取消 upgradeRecordService.cancelUpgradeRecordByTaskId(id); } @@ -98,11 +102,10 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { @Override public void updateUpgradeTaskStatus(Long id, Integer status) { - upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder() - .id(id).status(status) - .build()); + upgradeTaskMapper.updateById(IotOtaUpgradeTaskDO.builder().id(id).status(status).build()); } + // TODO @li:注释有点冗余 /** * 校验固件升级任务是否重复 *

@@ -123,6 +126,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { } } + // TODO @li:注释有点冗余 /** * 验证升级任务的范围和设备列表的有效性。 *

@@ -135,6 +139,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { * @throws cn.iocoder.yudao.framework.common.exception.ServiceException,抛出相应的异常 */ private void validateScopeAndDevice(Integer scope, List deviceIds, String productId) { + // TODO @li:if return // 验证范围为“选择设备”时,设备列表不能为空 if (Objects.equals(scope, IotOtaUpgradeTaskScopeEnum.SELECT.getScope())) { if (CollUtil.isEmpty(deviceIds)) { @@ -149,6 +154,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { } } + // TODO @li:注释有点冗余 /** * 验证升级任务是否存在 *

@@ -167,6 +173,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { return upgradeTask; } + // TODO @li:注释有点冗余 /** * 初始化升级任务 *

@@ -177,6 +184,7 @@ public class IotOtaUpgradeTaskServiceImpl implements IotOtaUpgradeTaskService { * @param createReqVO 升级任务保存请求对象,包含创建升级任务所需的信息 * @return 返回初始化后的升级任务对象 */ + // TODO @li:一次性的方法,不用特别抽小方法 private IotOtaUpgradeTaskDO initOtaUpgradeTask(IotOtaUpgradeTaskSaveReqVO createReqVO, String productId) { // 将请求参数转换为升级任务对象 IotOtaUpgradeTaskDO upgradeTask = BeanUtils.toBean(createReqVO, IotOtaUpgradeTaskDO.class); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml deleted file mode 100644 index 74fa85eaca..0000000000 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/ota/IotOtaUpgradeRecordMapper.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file From 9dfe2f6fdfdadd398890fe9a1bfde2036f2288e7 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 21:33:47 +0800 Subject: [PATCH 298/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AOTA=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application-local.yaml | 30 +++++++++---------- .../src/main/resources/application.yaml | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index aea19a5e9d..b4447b07fb 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -45,16 +45,16 @@ spring: primary: master datasource: master: - url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 - # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: ruoyi-vue-pro - password: ruoyi-@h2ju02hebp + username: root + password: 123456 # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -63,11 +63,11 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true - username: ruoyi-vue-pro - password: ruoyi-@h2ju02hebp + url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true + username: root + password: 123456 tdengine: # IOT 数据库 - # lazy: true # 开启懒加载,保证启动速度 + lazy: true # 开启懒加载,保证启动速度 url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro driver-class-name: com.taosdata.jdbc.rs.RestfulDriver username: root @@ -76,12 +76,11 @@ spring: validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 - data: - redis: - host: chaojiniu.top # 地址 - port: 6379 # 端口 - database: 15 # 数据库索引 - password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 +# password: dev # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### @@ -186,7 +185,6 @@ logging: cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG cn.iocoder.yudao.module.ai.dal.mysql: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 - com.taosdata: DEBUG # TDengine 的日志级别 debug: false @@ -266,7 +264,7 @@ justauth: prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 ---- #################### iot相关配置 TODO 芋艿:再瞅瞅 #################### +--- #################### iot相关配置 TODO 芋艿【IOT】:再瞅瞅 #################### pf4j: # pluginsDir: /tmp/ pluginsDir: ../plugins \ No newline at end of file diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 28b1eb60a6..3232bc2a92 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -334,6 +334,6 @@ yudao: customer: E77DF18BE109F454A5CD319E44BF5177 debug: false -# 插件配置 +# 插件配置 TODO 芋艿:【IOT】需要处理下 pf4j: pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录 \ No newline at end of file From 8203e074ac6e3e4520dcb07395140ae9979a5dd0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 22:28:10 +0800 Subject: [PATCH 299/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AOTA=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao-module-iot-plugin-http-1.0.0.jar | Bin 13508 -> 0 bytes yudao-dependencies/pom.xml | 12 ----- .../yudao-spring-boot-starter-mybatis/pom.xml | 1 + .../banner/core/BannerApplicationRunner.java | 4 +- .../core/handler/GlobalExceptionHandler.java | 6 +-- .../IotDevicePropertyGetReqDTO.java | 1 + .../upstream/IotDeviceEmqxAuthReqDTO.java | 1 + .../upstream/IotDeviceTopologyAddReqDTO.java | 1 + .../yudao/module/iot/enums/IotConstants.java | 45 ------------------ .../IotDeviceMessageIdentifierEnum.java | 1 + .../enums/plugin/IotPluginDeployTypeEnum.java | 15 +----- .../iot/enums/plugin/IotPluginStatusEnum.java | 23 +-------- .../iot/enums/plugin/IotPluginTypeEnum.java | 12 ----- .../iot/enums/product/IotNetTypeEnum.java | 2 +- .../product/IotProductDeviceTypeEnum.java | 2 +- .../enums/product/IotProductStatusEnum.java | 2 +- .../enums/product/IotProtocolTypeEnum.java | 2 +- .../enums/product/IotValidateTypeEnum.java | 2 +- .../rule/IotAlertConfigReceiveTypeEnum.java | 2 +- .../rule/IotDataBridgeDirectionEnum.java | 2 +- .../iot/enums/rule/IotDataBridgeTypeEnum.java | 2 +- .../rule/IotRuleSceneActionTypeEnum.java | 2 +- ...TriggerConditionParameterOperatorEnum.java | 2 +- .../rule/IotRuleSceneTriggerTypeEnum.java | 2 +- .../IotThingModelAccessModeEnum.java | 2 +- .../IotThingModelParamDirectionEnum.java | 2 +- .../IotThingModelServiceCallTypeEnum.java | 2 +- .../IotThingModelServiceEventTypeEnum.java | 2 +- .../thingmodel/IotThingModelTypeEnum.java | 11 +---- .../admin/device/IotDeviceController.java | 4 +- .../vo/group/IotDeviceGroupPageReqVO.java | 2 - .../admin/ota/IotOtaFirmwareController.java | 4 +- .../ota/IotOtaUpgradeRecordController.java | 7 ++- .../ota/IotOtaUpgradeTaskController.java | 4 +- .../firmware/IotOtaFirmwareCreateReqVO.java | 2 +- .../vo/firmware/IotOtaFirmwarePageReqVO.java | 2 +- .../ota/vo/firmware/IotOtaFirmwareRespVO.java | 2 +- .../firmware/IotOtaFirmwareUpdateReqVO.java | 2 +- .../record/IotOtaUpgradeRecordPageReqVO.java | 2 +- .../record/IotOtaUpgradeRecordRespVO.java | 2 +- .../task/IotOtaUpgradeTaskPageReqVO.java | 2 +- .../upgrade/task/IotOtaUpgradeTaskRespVO.java | 2 +- .../task/IotOtaUpgradeTaskSaveReqVO.java | 2 +- .../vo/instance/PluginInstancePageReqVO.java | 3 +- .../vo/instance/PluginInstanceRespVO.java | 13 +---- .../category/IotProductCategoryPageReqVO.java | 4 -- .../vo/product/IotProductPageReqVO.java | 2 - .../vo/databridge/IotDataBridgePageReqVO.java | 4 -- .../vo/databridge/IotDataBridgeRespVO.java | 1 - .../vo/IotStatisticsSummaryRespVO.java | 4 +- .../thingmodel/model/ThingModelParam.java | 2 +- .../thingmodel/vo/IotThingModelPageReqVO.java | 4 -- .../dal/dataobject/device/IotDeviceDO.java | 2 - .../dataobject/device/IotDeviceGroupDO.java | 2 - .../dal/dataobject/ota/IotOtaFirmwareDO.java | 2 - .../dataobject/ota/IotOtaUpgradeRecordDO.java | 2 - .../dataobject/ota/IotOtaUpgradeTaskDO.java | 2 - .../dataobject/plugin/IotPluginConfigDO.java | 2 - .../plugin/IotPluginInstanceDO.java | 2 - .../product/IotProductCategoryDO.java | 2 - .../dal/dataobject/product/IotProductDO.java | 2 - .../dal/dataobject/rule/IotAlertConfig.java | 2 - .../dal/dataobject/rule/IotAlertRecordDO.java | 2 - .../dal/dataobject/rule/IotDataBridgeDO.java | 2 - .../dal/dataobject/rule/IotRuleSceneDO.java | 2 - .../mysql/ota/IotOtaUpgradeRecordMapper.java | 4 +- .../iot/dal/redis/RedisKeyConstants.java | 2 +- .../mq/producer/device/IotDeviceProducer.java | 2 +- .../rule/action/IotRuleSceneAction.java | 2 +- .../common/pojo/IotStandardResponse.java | 2 +- yudao-server/pom.xml | 20 ++++---- .../server/controller/DefaultController.java | 2 +- .../src/main/resources/application-local.yaml | 2 +- 73 files changed, 73 insertions(+), 228 deletions(-) delete mode 100644 plugins/yudao-module-iot-plugin-http-1.0.0.jar delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java diff --git a/plugins/yudao-module-iot-plugin-http-1.0.0.jar b/plugins/yudao-module-iot-plugin-http-1.0.0.jar deleted file mode 100644 index 2d1f7be1b0b03aaec502df04dd6137a907975dd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13508 zcmb_@1z1(v^ET3Pl%HRj_V52~ zo_!AIVa+?U=FFa1v(}5O1Oy~J*wlNMo+{dE{D*v8$k z)G#i?5DqQ~Z@u2H(azEl*ClZhWa zy`<}(iM6q^DOI7`4bZa_NVEsrT?{C-IlhohckF!ks(x5aViOVC0Xmhuc$Cm?EJh&B zDw)zQuTMONMK}^o%^fSn<%T=?b~mgdq9xz0W4vWM{W`MYH80q2-T(tLc=>y$LEL!* zX!YG)e;k1DZGfpY(AvPz?r#Pn|9+sWqk*3F-wen4v*DK3296eneJ8vhL#%YOhfuy(excd#?m zv-}%CrvCuwX!8fSpM#6@e}S{Jc69h5&|rUg&KKC5(SpIjz%Ze}z{LIo>aQ*ovvv@@ z`(Bg{?HpWWfBB+GndpHQdiM4uic$)w{79Zaa5z1<2k5f05BNX((Aq@EqeCDUfy;~3 z%dp28*i==qar4(-B=VfQmh*hcrgcY=EqcaKuEEVaKYo4bC^LL@b+XI-VoQ~a9@vEVF%W+Bfc(j4zDd%YcZ(dY>jRRr$kVgg{HmMQaNVFs1PUL^DUu{&k;li7Vb zhGHf_a&0(lp9U)37f{Wi|R6XSUW7FL(KZB@06p?nVdbP$qmz=%P~3g9~F6# z@+e5I@^y-)can3w&UyrdMnrMhUPd;;uC*>A007miMHw6-+C22oMl~BntBfp?Rt=M# z=3|qb4`xn9xF(3#)tfP-=(5-a$=o3xCUF}CC&KyYRZ~M$Oz%zvcmb<)`#!;sD@S-* zNj$?A0u&~HFq2Wt(MoRy)azG0E-q<1QWA1KAwp3#qSQ($^+T@*C=i^4BC9xFUp{ra zxI)r8rD7&M6Edh(61o|ZMuJ`Zh=;6~5GfVAhmz1gL*s8$w~gBa-Y=Xy7+0OF*m8hO zdy5@{IE5o)f=3Exp&QckAkgOwyK7fEcCOKiScHKw?$$r%!}$#6!i`VGV#oR0jk2Uj zpSH~#UnyCa49PX}v6-xFxpr1<6#d%g)ImJ`obWE8lWBP`*d)TQ==j~dAb!ix-@+aB z^xB)`F5Cqn!N3&$r*Quk##857RI@ZjQUBik(R>1mem6_^(;RA$C9%z4%(PK4-PmJUH`EcJH1=S0R0J8gLdpA_sp`0x>Ijt3 z0=JXn4&_7 zYpyzrK?nOwtaI)N+f)eY(^i^Y7Jey4$sj>5h|Z?D(Q|^N>!+yvv~NC+S-!FnbkTcVoilAv ze=8dJMvyJ1-{y187lpSlq%<$-E~u7hq{PXk&qoXVW5ttG(_Q#%$@AV?#?3wBlh>{c z@arw!GY(qW)SJJcY4CA9$Ax9Abb#wi&nUC~tZ*z%!727&lYwGZ0WFzqxG#!+-`BH` zStgFoU>S0jC5fMY-VOVr*lB|$lp*1%{-*(Y@}+%=ClZKCK+fkw=~`sxJR5`L^)DJA zF->`8&SB{dZPRPPJYaqwlf=M`xYWCNbh}$;J^!C#@~0I1YaA+RDBuaB-QZvtsgV;A zEeN2H!TAS7aFa*-!)Db7LaNb2Ln6xs%^G22<<%LvLAaI=fWpv0%o|lo$`>e}<*e$$ zu*lvoS|LVs^jzJX66doF-|@VA_2ld*l`r4>*8G|SjG;3!12Pv!fNMZ@$VT18l!Rrv zLH8Vc*lD~+^_fVj#herNfV;(X!dpOoRzLpWYnw3^7VJI8Iw{4+;qVWU@n4Q$U(!yO z2aw4xutw5 zr5ZC0#psa>EZu>CW`V($n%D8FxiE12coU^@)-r#2l3fCm-n(}AN*u4#_TeO#bVlIH z4F4H`Xg(DcP&2yj>)OkTr@&8)PO50)4_`7a7X>{gYCoIU&sl3Iu2B!SBHlubP5*q} zP7Q{0$aND%2V*IVTA2!~n_W?XL=gDs6;GL|{chOPr;9V18nHa8iw#n|dglT9BMC&Gi@IDBoCzK=={NTyl0nX<)#KEp_`pU~3$;7}p2!9)|j znZMV4YJ66==b7!6xdPr9qLt5~Oq(WG?vHG^eKTxT*l|L_Gj=7ad1hFAsq(!LXjRB=)c#fl5-ZebO7>KnDQOUpdM8 zRkgs!tnr}za>%6#TU=l@1`pqst#{g~rJ~ppcrI(r;W;IV57oc*!)PqM`8nt4xQKk4bR3GR!wm5>c`HRl&&Eg||7>V_{391F%n zi>E#ne8cw{ONq=xN7#~C*|X9CB7e%pNM39RCKd)V@6tSiS44w~AT|m%c{CK`GZQK) zN}e-`?QlluUZO}Wb;DTjD!63;rR3O%Z0yvCyx1cnXZe*2#bl*mOy3tDKr@(^V@o4! z4lgkTI8bofgjcnRyfDQ|vmQ*_5KJQwQTRm@sgm{u;1QW9_DxZ2Cz*Ns)AVC0y{`#P zQ)a4CyB4!yn2nw1|@@DN=AXAcWof5?D8EWPg* zqC$pFra(iX@5^yfJu3qXLpw1`8;frlaq#7!G#WqBi#KYzGmhvs!tGDJHuiO&wh@O2 zOh_rWYjwPxr3wmsRa%h`QyHb_0kbB@YqHu7bC4O%M$%*gSqW#xDXHQtH94&`$ujxA z#rpyt3{kfM(o^;zqZI(bNU3WegHMS~joKHH%4_lDfgjDBNgZ`Yd408(KM|#yEOjRt z<*m_@1;rzh+N}80o(~teQg-8JYso`dp+#i%;GG2F@p`QD#df|CU<^abnTHv~%fg^? z{xFZ~%GAqH10gRQVw%+OPSz2TJuXa%^hZ*$s0I-WTm;(~rOu8rFAoK)YbYP_Vxg9g zLevV&3FUE=zqnxwgu^N|_ZuEJf!R%%gHux&-F$I+m=8p?B!_HPD^5rE4Xru0N8I6F zl?|^#OTz*oS0?O_wW)m6z@5x}y24NrdJz4|i=r&)RJ3&C2%6k{Rj^h*vuGwW==d*|F@|LSN5l!6Ersap(zT#Xf+Y$A-?S!v4GNf-4c(lL*d z&SG$cef_eOz{o@_4<33Bdrba`a!r=z`&Ww1w51Y`gVf@MB_4w=&GLtuDdsAd&|Fwt z-qA+9+9f!Q*wQmC8{!%X{*^|4D2d8y3E(o(avJCiHk$8~#~Srhb2cn2TiXb@)Ymg1 zpc4W=aAP;ds@OejVJ;K^fMOWSJG#Ujn2xWNugT;zg*^0XiLtU!jmdkkoHlP(l*lKl z=elr;$)uDrQ;-o!Z$p@sV8z@ei#y$@1K>RZ?VkpuOcN=PPsUFrv|w;lzkGR<^AR!> zleeNz*lC3ms0-2A-&!ImTWCh+GRsAFQjlb8X4nf&aWflKWD0>g7|1naPNw zI>N!H&DR(0*A33jTYJ2(noe~04pWCY_PZ-kn*gfh(B&z7GT9JjgwyHqdO&t6G$jWH z=!c(JDhOe)lsWhaw`vp25{N|Fy@3`uHw-aM$FC7Rb`!d(yn9L-^^`qzKBzyNWW-LR z#oS=~=zkEfsvf^B#wx*1WXQsV-3R+?q@ z@yF8yWq9_Tept&UVSq3Uv*j>YxD0x|4}Z;ATmmC!g3@uYe~XC7da^Gd7;-6#b1};w z=*A*lg{ZszXx{(e@$MWfB62NK?UYlHXN=%P+qoLbu3j9|n`MD!KF&hf=po7U)%gVQ&)N;UU37|xwiZJ?GEONnn zK>fao@IoonLf<`j5&$qT>HqNXecx~SUPF|A-GNasv~x1F`*k_3tY(joS2Z%x-id&zJ2Hv<=oGk6CQxnI#8?^7AynnK_q;qq5^Bj!(pn(vIGG3Mx_$U+` zBfQX7X;g_M3l9r>jVLIyUX6t{KOmD)mPHkuP@MGKFkZlD8-N{Z6!?y@F5Z@-I9YrG z(8QIW7>P2z7;HqD9f_*2khH6+89}X;R~6J-gY-1sZG5_X1bI+VwEIb+zhoGV-)e%MG*1Kl!wJYESXQ|8jgAiez;0JE@d`Bb zyMDf)%Z@odVTA&J1CmhgQgRwuHmqb~Fz=bR;5gWo!lb<%8IVjFS$7y-&`_7$6{V~P z2M)1cHOvuUZr>%ML{hA<=bW-~IJC~&jv%KUuZZa@E!pf;Q&*krlpDiz2=6uFiHl)E_9zm;}xR<9Gtq?4dXfZNb(k~W^p;FB;#1lD3WV?2}; zP1eliC>>CIBs>^-xs%Uqo@ic<&e+A|^UZ8OZX!k5;_K$)4I3)o?Sv7MH83|S;A~@8 zfe{{?7%ySApVx7wPREOIK`aB+Bo;3$kK`3VQ6uOr_6*tBm-=uL7vHQ2w)qw4&dO&h zPEK5!y){CpPeb(JWcpojh8g3xP>EY3XN^RFT+XcH+s+fBMc%xerP({AR{okGl$sPW zF?tcCSfqq@+xX2ci+GdBJgp)Uu5@tuFAUNwm5L|XBc$Q|oY1Ly*PkYWqnH%3<6p|S zCf6<^jUF3HC7VR8_FI=;Ijzj4pOszROb+(H(SejsEyTtk?J~f_e0?z)y;kJL@DQ>+ zkFL{4Vw{?ullvK*8gXBLT^XdUK>R~XT-?uS&*>YJ`3cj=*8PXWb3Yzj?LIB_-X-Fq zTLYwJ&k~GDr4X0&?Mynzz3Vh3)J`%2RmB2c4r~aoQmnOH8wB@4I`X|9mTT~~veWM{ z_fs*@Y!kK{rUO+&CL#jNbSIc)VD%~K;Sx=020T96KYSkcxf=MCenp9CMH^LaP`4|} z+(oMcmwBm6quS4?P9Fx@P<;(RhP)@~xYf8bHRmu{YdW+J01Mop64esXtYWj9F~iEs zhPSCr!fwRIXNsb7Z)m5(d8H@6gSf7D?JaFg{hocyFo|#iu(T%fyq@jxYX0c>V5%I; z!W;GK*^yG=a*V(#DI{)f+c^Ao;!aJo-jNC`wuH)6^G7>{sEMub9ZHb)N4mbCv*4w& zha&A{EN92z$qEHw;^gYOq@;zKdIn7~+wL7ICl4-(_j1T2FE4YMwn)fe?lc5X`PH1Y znSI=QR+QSdz^$tI1s_97S4>6_oCb@$oXh6~igQ7>mdQs7>iogU^AG!a5}z$gqNR0l zAwoFNmmjHH$H)bqZ8s8-Ih2rcfObmZ5z67wRi(C~%q%)_xME6$8Kzb|W2vbg$G5?x zeGby51HN&5kf-&l2{wsb_wKa zK%cHUTu7-SQi=;14(zCiK<48YQYP(2v6qy)S1NY&W5saUYo=RIvzd36(Li?eu`yTT zPh9!FyeYSvsm@GsbSMV+pw6!zk_;3j(r8ZH zHwdXlS{m?ppM2SJMw)j288@%vNLk#mpFt)9#LV-i5wGO=#dFmhEMcFb41fC#MZWZ$ z?BY`t$#jqhYP}oo?kfXy_w<47OR5s6^H0{luQ(-nW&#>_XJ!?cU|@RxOU3zhOa6P| zk+rk7x!X#2l{d7pwsZJpSN+#6=35b}QHAqBo5a3t9b`%|Tvsil&!7#i7ZVjxABK#N zwkfs{AWaNbq-bJDA!=d)y|WN?bE{FJ6|gid$(<@f+s(vlB-N)oEypOS$+z6q3aPHC zmNpwQ7ckCI^tLEg z_N2Zt&z>Y|4v-thrd7oNq=bK2m)IW049AD9@R+4I>O-iJPy`mo%Nqwtw)h-m2?|cS zIdSTkNb|-q%XuoY)JluEH)7qaW9-O551V|>%;c6VaHy>2uFYKs7o8p8$-kX$@Jk}|cL1qEk0!5gj7!Wn~-{2q6H3JJzGm~8A| zK3)N(a3{M@Fg8@iEK!q?*6_XSe1ftG;>1M28X`q?&bRCoHK5X%Vy&4~_iay+mkJxF zhc>J}RpZ-;0q3sTC-ujY#-FWhW*AVYHQEN3MrWq3h+cllxC*!l*6Ts18Fg$IA=B?# zTOcJ-W>GBF5$42+h`c z^Bq#^+9_nY?!^z(^ee*29$Rxc%QyAG(OT8b6s6XFfIAadKQNFv)~9;V(OzbyQgfPL zj|qa^F{6Gtl2ud>wpC>2H)>^DcN$weg|3tc%a3b7+Y9e=QT;ab4U=AVPe{beBj0|w ztxi`}GLmqbKEc%mtM!E39v-1-9RK*y{;Dq<5R?^1qIuGqnVWd$2R0d`~% z&iF(S{}w>VW%JVCk+~G(6b>&jw%i1lUeMNZx^L!1U0kwe6uQXS`A4;w+VQ*>1C9U! zI`T(*@yJk?3=s(Huhx9Eh8B-hCARfV(hY(mXQAOT=E-;BB_1`@(}Bsfb8l)6<^@i% z@!6nVWawr46JW#Xy?jNYY^#`Qk}BK5#Gx*=D^J%XERl!OTtc_-`1B()Ag7N6c4syT zo1BdUy?RVcjwI2G%TzJD_6-7dO1L^LUMCE$DcoCTc7%R%$^6@`!qTNdY3aOATY^uQ zh*naWmjayQWS)?h*O2C8VJo4=+q=?S6I1T=7zL_zO23kkr-^qd3;$^Ahg-6sxVN{5 zfbTq?Vh$vlkYcvNFhUn#+xpz%a^!_gh~+gN4H`16N7o=-AYb#F8Ia|U@z9i^C~0=< zY~QtW6JDyyqe8J#_sS~Tfb+x zpXT{%0D1FR@$0c!(I~Bg*D~QCXvmaEj|5kz7Rw=3P%=hkUyGTyw1fzCE9J)PcZ0M z@}{eaXDznsp-RW@YHjm5Tc>d7q%{>AiBy?9%UC$IbYU@PZY8rOHBFYHkt&yIFXfDR zVqIK;RVy=H)=o23>Zf(1Hzv6Sgys(P!#g<*Qi;#A6lfz)WuMuIl3qEzON3Ps6i}Yn zLoF-=i`?efUo|2AlABd59QLt@(gK_KVj{-(tm52Xv>`nFlR49vQGD5mKY6B@M^6Dp zZyXm0(20RAJ}|kItQ@N1d5(KSze1Q*aY5nZC?qen&*tW$Y+pCspLsAe3f+g&D7i>d za)I2rEtXHdC(vVo_Ezt~=Mz+zE7~wOwiuI9i&3Ggl;p@!tqTbVji5b`^S)D!z|(z~ zi_#Roj<f>5MJ->9jNOS&cUPPl+F(l;CITptHJ}94q00y(#cO z8`Hk*W)!;3M=@^CPdZQ3!_dZP!mdDuM2peHNGl-?PE3%0d7}W$H0akOe!DZ2Q)q`| z%shbx6AHr=rUzzkF78C4snCeHaZ~hc=W#qS(r5r$=IklI<_Di!JMYEEAHrn$X~Lnz zSyV5ulp$`&z3rjRSxgAK+z`1=p~bJJjWVX{PGP?|S z0IZ4!mOX>o&Q=ltR>RAY{2uYAO-)W?rA?_?>D2Y+#>&Tly+dy!Bt#d=GM=P%LBE;j zJe65%JBN3--}d9FSI-J>+g$pcfrb&Jw2WBMJ*vx-JP6HP9hK_75|~$a`Z=(I~CHQXs$)!BtfFO4FZD(?2En z1G}d0ye*ZJEUFiS*&$fTQ-2U+FzAppVHJB~1&X$lrur01kSry4M&Hfp@GvpkwZT6!Da7*b{ZnnjCWik z)3$te9M?4-X zsom#(z~HB=GDGLaz5%)&2P$qi!eE)Af>qOp(3Y@QnD;AN;2o36&G-wF^WBOz6?{zD@j%7_dP~F^e7rN=Ptwu_d?fY3<3UY(+6#958CK z0deXlC$P~b#{S+2@w;h)VHRX>=qfB@KgrZ!*<<2A@)#Ikj~<(;#X;us9cN`oPi$ga zo9EsS$412W2sXB{;ca+?C~Kz#x!(f%zTfjlirQBjzf;sK^_&cUBpm(BXCwUMV4xNK zuS6{RuY0dQFnN9!=J{vgUl>|+-zZmfrq&L0-zZRYCO)`7Bx!z*5iz5E&D1$4f9rYM48l}E-0Vs3Nd_Cyv6krHqu%{ZYlTm zJ~*Ffr`5KHH(;1utv(UyKD(q+Q)8v*S#@ViXKAer@2ha}hdw)ng zQ?0FKA6#?;j~RI)&xi}VXhbC$$1O7A;MR|YNGHzoLYVBHiAv|UF}<8fsE`HPc{zzc zvPC=t4;u0Id*`4|76YS?8{}iyb%FuxeA{-zg9v9?5xUcQf>W~U+@D~9_C3=WN4U%u&Un|J=#^ce@Du&6P4%g+?CorF<=ovtzR zKA)0^A-eqenQ3~$NqXss@iRWYX?0&Hdz9`pw80wW9_Sj}nU{guonueZ;`!y|EG4S$ z=@JabKHt^V&F&^>ODDY)Vx70Z9(l*99xh;DwI_#(B~ITTH6|_*D*sb#fxtWLWQ-S| zaH0+6X+n~7wN(msnpkK~QC!RtBYh&@=31I*lhq!#L)0B~h=ZVXoI0I3Fg^jBkW9`$ zjb0q|&vxUR^GH(&2w1>~CGZOsl@rS(%v#3+6Vc2IKUp@(PYuZzOrL!$!inxAY8n}M zAwU++4$oEYdwDAZgE|w*UKP>IyQ-ZRbpBxP7Ijiub6+v%8T??l>~1WbdT; zTdX9SsgNpYNT;@FW{F24!*LKfa>YAeZ z5cQKtw;5&%wQ%NyPw&L+sZWHk77Z*OapD_B_o%RrPvw$ZsP~CSDJU~ISVc^V*o?fn z3?x8#?ey3@m#gTcq%sOhEqqZe!xvBxsC7(N=dezfQ>MgWfjl2-9%wZ(6>@$0Jm-cZ z>g?u+gOaatPaI(Q(Dg3fU)DUptRszv z!H~XCAq>Qq!B04;Gn~yf{NF5Kr z(IEbsL3=mLy9)_ym;dYg_hfZ1_UDY+uY|cX_Wx?KqfBp~PKOD^e=;WWR|H_KJS0H#7tpCEvzxd?) znaz*zKgaS{mh3%z?EeD)oiqEJm;RhJ`AQ2Uf9tz@ zwg1Rv{Mmq?scQEInBS4t{<{HR&%J-(4u9L^{8{PG?2vn|bz2em3Ig zll^-m*zYpmzn%Hl`TpZM|Iey_KG(cgU3K>_7ynlE_b2B2?EmbP^Jm>Z?{MGiCd2(7 zbpO}=?jOzhv$o{koLjO#G%5GOf8H;A6Gj5Nz0D^7p|bwgJ^c2>|55mB|8Orn@<-u6 zb`yUM_O+k52YYx|Mr*@qxt&j{{iGQ^WOjf diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml index fa54eef606..4d2aef3415 100644 --- a/yudao-dependencies/pom.xml +++ b/yudao-dependencies/pom.xml @@ -35,8 +35,6 @@ 3.3.3 2.3.2 - 3.3.3 - 3.4.3 2.2.7 @@ -292,16 +290,6 @@ rocketmq-spring-boot-starter ${rocketmq-spring.version} - - org.springframework.kafka - spring-kafka - ${kafka-spring.version} - - - org.springframework.boot - spring-boot-starter-amqp - ${rabbitmq-spring.version} - diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml index d5b1893919..5a619c5118 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml @@ -68,6 +68,7 @@ taos-jdbcdriver true + com.alibaba druid-spring-boot-3-starter diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java index c8b0dbd66e..d07c4aaedf 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/banner/core/BannerApplicationRunner.java @@ -62,9 +62,9 @@ public class BannerApplicationRunner implements ApplicationRunner { if (isNotPresent("cn.iocoder.yudao.module.ai.framework.web.config.AiWebConfiguration")) { System.out.println("[AI 大模型 yudao-module-ai - 已禁用][参考 https://doc.iocoder.cn/ai/build/ 开启]"); } - // IOT 物联网 + // IoT 物联网 if (isNotPresent("cn.iocoder.yudao.module.iot.framework.web.config.IotWebConfiguration")) { - System.out.println("[IOT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]"); + System.out.println("[IoT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]"); } }); } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java index 41c5cead6a..b0ca9c7783 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java @@ -378,11 +378,11 @@ public class GlobalExceptionHandler { return CommonResult.error(NOT_IMPLEMENTED.getCode(), "[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]"); } - // 9. IOT 物联网 + // 9. IoT 物联网 if (message.contains("iot_")) { - log.error("[IOT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]"); + log.error("[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]"); return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[IOT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]"); + "[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]"); } return null; } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java index b72d88d974..d9ae963214 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java @@ -6,6 +6,7 @@ import lombok.Data; import java.util.List; // TODO @芋艿:从 server => plugin => device 是否有必要?从阿里云 iot 来看,没有这个功能?! +// TODO @芋艿:是不是改成 read 更好?在看看阿里云的 topic 设计 /** * IoT 设备【属性】获取 Request DTO * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java index 2fb10a0765..8762aae5bc 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEmqxAuthReqDTO.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.NotEmpty; import lombok.Data; // TODO @芋艿:要不要继承 IotDeviceUpstreamAbstractReqDTO +// TODO @芋艿:@haohao:后续其它认证的设计 /** * IoT 认证 Emqx 连接 Request DTO * diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java index 38b2b69ef4..18efe7d48f 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceTopologyAddReqDTO.java @@ -5,6 +5,7 @@ import lombok.Data; import java.util.List; +// TODO @芋艿:要写清楚,是来自设备网关,还是设备。 /** * IoT 设备【拓扑】添加 Request DTO */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java deleted file mode 100644 index 631ca24a36..0000000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/IotConstants.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.iot.enums; - -/** - * Iot 常量 - * - * @author 芋道源码 - */ -public interface IotConstants { - - /** - * 获取设备表名 - *

- * 格式为 device_{productKey}_{deviceName} - */ - String DEVICE_TABLE_NAME_FORMAT = "device_%s_%s"; - - /** - * 获取产品属性超级表名 - 网关子设备 - *

- * 格式为 gateway_sub_{productKey} - */ - String GATEWAY_SUB_STABLE_NAME_FORMAT = "gateway_sub_%s"; - - /** - * 获取产品属性超级表名 - 网关 - *

- * 格式为 gateway_{productKey} - */ - String GATEWAY_STABLE_NAME_FORMAT = "gateway_%s"; - - /** - * 获取产品属性超级表名 - 设备 - *

- * 格式为 device_{productKey} - */ - String DEVICE_STABLE_NAME_FORMAT = "device_%s"; - - /** - * 获取物模型消息记录设备名 - *

- * 格式为 thing_model_message_{productKey}_{deviceName} - */ - String THING_MODEL_MESSAGE_TABLE_NAME_FORMAT = "thing_model_message_%s_%s"; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java index 5bd169abae..6de9359ba0 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceMessageIdentifierEnum.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.iot.enums.device; import lombok.Getter; import lombok.RequiredArgsConstructor; +// TODO @芋艿:需要添加对应的 DTO,以及上下行的链路,网关、网关服务、设备等 /** * IoT 设备消息标识符枚举 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java index 3f7e75310d..b6ef4f0cc3 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.enums.plugin; import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.util.Arrays; @@ -10,6 +11,7 @@ import java.util.Arrays; * * @author haohao */ +@RequiredArgsConstructor @Getter public enum IotPluginDeployTypeEnum implements ArrayValuable { @@ -22,24 +24,11 @@ public enum IotPluginDeployTypeEnum implements ArrayValuable { * 部署方式 */ private final Integer deployType; - /** * 部署方式名 */ private final String name; - IotPluginDeployTypeEnum(Integer deployType, String name) { - this.deployType = deployType; - this.name = name; - } - - public static IotPluginDeployTypeEnum valueOf(Integer deployType) { - return Arrays.stream(values()) - .filter(value -> value.getDeployType().equals(deployType)) - .findFirst() - .orElse(null); - } - @Override public Integer[] array() { return ARRAYS; diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java index 9b187c5b66..7e3fa657e2 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.enums.plugin; import cn.iocoder.yudao.framework.common.core.ArrayValuable; import lombok.Getter; +import lombok.RequiredArgsConstructor; import java.util.Arrays; @@ -10,6 +11,7 @@ import java.util.Arrays; * * @author haohao */ +@RequiredArgsConstructor @Getter public enum IotPluginStatusEnum implements ArrayValuable { @@ -22,35 +24,14 @@ public enum IotPluginStatusEnum implements ArrayValuable { * 状态 */ private final Integer status; - /** * 状态名 */ private final String name; - IotPluginStatusEnum(Integer status, String name) { - this.status = status; - this.name = name; - } - - public static IotPluginStatusEnum fromState(Integer state) { - return Arrays.stream(values()) - .filter(value -> value.getStatus().equals(state)) - .findFirst() - .orElse(null); - } - @Override public Integer[] array() { return ARRAYS; } - public static boolean isValidState(Integer state) { - return fromState(state) != null; - } - - public static boolean contains(Integer status) { - return Arrays.stream(values()).anyMatch(e -> e.getStatus().equals(status)); - } - } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java index 0f81d5a8f7..ec0b72f9fd 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java @@ -24,7 +24,6 @@ public enum IotPluginTypeEnum implements ArrayValuable { * 类型 */ private final Integer type; - /** * 类型名 */ @@ -35,15 +34,4 @@ public enum IotPluginTypeEnum implements ArrayValuable { return ARRAYS; } - public static IotPluginTypeEnum fromType(Integer type) { - return Arrays.stream(values()) - .filter(value -> value.getType().equals(type)) - .findFirst() - .orElse(null); - } - - public static boolean isValidType(Integer type) { - return fromType(type) != null; - } - } diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java index 561bc66f92..2a54e489f4 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotNetTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 联网方式枚举类 + * IoT 联网方式枚举类 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java index 238e3e25ff..7910f1b2d1 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 产品的设备类型 + * IoT 产品的设备类型 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java index ee8be5c81e..b9bbbeec76 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 产品的状态枚举类 + * IoT 产品的状态枚举类 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProtocolTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProtocolTypeEnum.java index 9eb57044fb..d24dea92ee 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProtocolTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProtocolTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 接入网关协议枚举类 + * IoT 接入网关协议枚举类 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotValidateTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotValidateTypeEnum.java index 11604b4dd8..2a15d16a4b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotValidateTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotValidateTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 数据校验级别枚举类 + * IoT 数据校验级别枚举类 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java index 87df89f763..3fdd53234b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotAlertConfigReceiveTypeEnum.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; /** - * Iot 告警配置的接收方式枚举 + * IoT 告警配置的接收方式枚举 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java index eb4b999163..a9d445fd23 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeDirectionEnum.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; /** - * Iot 数据桥接的方向枚举 + * IoT 数据桥接的方向枚举 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java index 25c7e8c1fe..78fc8452eb 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotDataBridgeTypeEnum.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; /** - * Iot 数据桥接的类型枚举 + * IoT 数据桥接的类型枚举 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java index 2dfb92f636..2bdf7d0ede 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneActionTypeEnum.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; /** - * Iot 规则场景的触发类型枚举 + * IoT 规则场景的触发类型枚举 * * 设备触发,定时触发 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java index 1aac8c2371..5ed90ccae7 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerConditionParameterOperatorEnum.java @@ -8,7 +8,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; /** - * Iot 场景触发条件参数的操作符枚举 + * IoT 场景触发条件参数的操作符枚举 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java index 509b9a6032..a420a21d5b 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotRuleSceneTriggerTypeEnum.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; import java.util.Arrays; /** - * Iot 场景流转的触发类型枚举 + * IoT 场景流转的触发类型枚举 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java index a78614853f..c0a2b329b6 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelAccessModeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 产品物模型属性读取类型枚举 + * IoT 产品物模型属性读取类型枚举 * * @author ahh */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java index 00158a0f9b..4f06cefcec 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelParamDirectionEnum.java @@ -8,7 +8,7 @@ import java.util.Arrays; /** - * IOT 产品物模型参数是输入参数还是输出参数枚举 + * IoT 产品物模型参数是输入参数还是输出参数枚举 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java index d6ed70e502..376db6b4a9 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceCallTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 产品物模型服务调用方式枚举 + * IoT 产品物模型服务调用方式枚举 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java index 584c0743fd..c7c74092aa 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelServiceEventTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 产品物模型事件类型枚举 + * IoT 产品物模型事件类型枚举 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java index 8f0345529b..e0097cfe92 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java @@ -7,7 +7,7 @@ import lombok.Getter; import java.util.Arrays; /** - * IOT 产品功能(物模型)类型枚举类 + * IoT 产品功能(物模型)类型枚举类 * * @author ahh */ @@ -30,15 +30,6 @@ public enum IotThingModelTypeEnum implements ArrayValuable { */ private final String description; - public static IotThingModelTypeEnum valueOfType(Integer type) { - for (IotThingModelTypeEnum value : values()) { - if (value.getType().equals(type)) { - return value; - } - } - return null; - } - @Override public Integer[] array() { return ARRAYS; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java index 2add4ee133..08fc244b15 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java @@ -133,8 +133,8 @@ public class IotDeviceController { public CommonResult> getSimpleDeviceList( @RequestParam(value = "deviceType", required = false) Integer deviceType) { List list = deviceService.getDeviceListByDeviceType(deviceType); - return success(convertList(list, device -> // 只返回 id、name 字段 - new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); + return success(convertList(list, device -> // 只返回 id、name 字段 + new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName()))); } @PostMapping("/import") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java index 1490f2894e..93b1a1eadf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/group/IotDeviceGroupPageReqVO.java @@ -13,8 +13,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Schema(description = "管理后台 - IoT 设备分组分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class IotDeviceGroupPageReqVO extends PageParam { @Schema(description = "分组名字", example = "李四") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java index 344a1e3fc9..6cc3918e8f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java @@ -19,10 +19,10 @@ import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -@Validated -@RestController @Tag(name = "管理后台 - IoT OTA 固件") +@RestController @RequestMapping("/iot/ota-firmware") +@Validated public class IotOtaFirmwareController { @Resource diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java index 519d6b9ab8..f6bc526ac2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java @@ -20,10 +20,10 @@ import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -@Validated +@Tag(name = "管理后台 - IoT OTA 升级记录") @RestController -@Tag(name = "管理后台 - OTA 升级记录") @RequestMapping("/iot/ota-upgrade-record") +@Validated public class IotOtaUpgradeRecordController { @Resource @@ -33,8 +33,7 @@ public class IotOtaUpgradeRecordController { @Operation(summary = "固件升级设备统计") @PreAuthorize("@ss.hasPermission('iot:ota-upgrade-record:query')") @Parameter(name = "firmwareId", description = "固件编号", required = true, example = "1024") - public CommonResult> getOtaUpgradeRecordStatistics( - @RequestParam(value = "firmwareId") Long firmwareId) { + public CommonResult> getOtaUpgradeRecordStatistics(@RequestParam(value = "firmwareId") Long firmwareId) { return success(upgradeRecordService.getOtaUpgradeRecordStatistics(firmwareId)); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java index 5486102e6d..e248e80274 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeTaskController.java @@ -19,10 +19,10 @@ import org.springframework.web.bind.annotation.*; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -@Validated +@Tag(name = "管理后台 - IoT OTA 升级任务") @RestController -@Tag(name = "管理后台 - OTA升级任务") @RequestMapping("/iot/ota-upgrade-task") +@Validated public class IotOtaUpgradeTaskController { @Resource diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java index 20cd19536d..50c2ece155 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java @@ -7,7 +7,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -@Schema(description = "管理后台 - OTA 固件创建 Request VO") +@Schema(description = "管理后台 - IoT OTA 固件创建 Request VO") @Data public class IotOtaFirmwareCreateReqVO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java index 24304202ca..baa7410298 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java @@ -5,7 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @Data -@Schema(description = "管理后台 - OTA 固件分页 Request VO") +@Schema(description = "管理后台 - IoT OTA 固件分页 Request VO") public class IotOtaFirmwarePageReqVO extends PageParam { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java index f9aa25cca7..735618781a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java @@ -10,7 +10,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA 固件 Response VO") +@Schema(description = "管理后台 - IoT OTA 固件 Response VO") public class IotOtaFirmwareRespVO implements VO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java index 4a304338d8..aa134bceef 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java @@ -7,7 +7,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -@Schema(description = "管理后台 - OTA 固件更新 Request VO") +@Schema(description = "管理后台 - IoT OTA 固件更新 Request VO") @Data public class IotOtaFirmwareUpdateReqVO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java index 57335ddcbe..2b21b30796 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java @@ -8,7 +8,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA 升级记录分页 Request VO") +@Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO") public class IotOtaUpgradeRecordPageReqVO extends PageParam { // TODO @li:已经有注解,不用重复注释 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java index d717cfd310..db6737febb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java @@ -13,7 +13,7 @@ import java.time.LocalDateTime; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA升级记录 Response VO") +@Schema(description = "管理后台 - IoT OTA 升级记录 Response VO") public class IotOtaUpgradeRecordRespVO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java index c1f2816c06..d2b1926aa6 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java @@ -8,7 +8,7 @@ import lombok.Data; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA升级任务分页 Request VO") +@Schema(description = "管理后台 - IoT OTA 升级任务分页 Request VO") public class IotOtaUpgradeTaskPageReqVO extends PageParam { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java index f8f5320c9d..dbc29618f8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java @@ -12,7 +12,7 @@ import java.util.List; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA升级任务 Response VO") +@Schema(description = "管理后台 - IoT OTA 升级任务 Response VO") public class IotOtaUpgradeTaskRespVO implements VO { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java index 687cf2b255..0ace17a047 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java @@ -14,7 +14,7 @@ import java.util.List; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; @Data -@Schema(description = "管理后台 - OTA 升级任务创建/修改 Request VO") +@Schema(description = "管理后台 - IoT OTA 升级任务创建/修改 Request VO") public class IotOtaUpgradeTaskSaveReqVO { // TODO @li:已经有注解,不用重复注释 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java index dad6926bdb..e58b88856e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java @@ -8,11 +8,12 @@ import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; +// TODO @haohao:后续需要使用下 @Schema(description = "管理后台 - IoT 插件实例分页 Request VO") @Data public class PluginInstancePageReqVO extends PageParam { - @Schema(description = "插件主程序id", example = "23738") + @Schema(description = "插件主程序编号", example = "23738") private String mainId; @Schema(description = "插件id", example = "26498") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java index 96a89299d3..cba59fdaf5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java @@ -1,43 +1,34 @@ package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.instance; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +// TODO @haohao:后续需要使用下 @Schema(description = "管理后台 - IoT 插件实例 Response VO") @Data -@ExcelIgnoreUnannotated public class PluginInstanceRespVO { - @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864") - @ExcelProperty("主键ID") + @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864") private Long id; @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738") - @ExcelProperty("插件主程序id") private String mainId; @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498") - @ExcelProperty("插件id") private Long pluginId; @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("插件主程序所在ip") private String ip; @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("插件主程序端口") private Integer port; @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("心跳时间,心路时间超过30秒需要剔除") private Long heartbeatAt; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") private LocalDateTime createTime; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java index 90d338a898..f1c12bf7cb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; @@ -13,8 +11,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Schema(description = "管理后台 - IoT 产品分类分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class IotProductCategoryPageReqVO extends PageParam { @Schema(description = "分类名字", example = "王五") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java index d54adec484..18c69c4cec 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java @@ -8,8 +8,6 @@ import lombok.ToString; @Schema(description = "管理后台 - IoT 产品分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class IotProductPageReqVO extends PageParam { @Schema(description = "产品名称", example = "李四") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java index 401f796f6f..7228b23bb9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java @@ -3,8 +3,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; import cn.iocoder.yudao.framework.common.pojo.PageParam; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; @@ -13,8 +11,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_ @Schema(description = "管理后台 - IoT 数据桥梁分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class IotDataBridgePageReqVO extends PageParam { @Schema(description = "桥梁名称", example = "赵六") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java index 1db10a762d..79c2c5cbed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java @@ -9,7 +9,6 @@ import java.time.LocalDateTime; @Schema(description = "管理后台 - IoT 数据桥梁 Response VO") @Data -@ExcelIgnoreUnannotated public class IotDataBridgeRespVO { @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java index 1b750f380b..21745c4abf 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsSummaryRespVO.java @@ -6,9 +6,9 @@ import lombok.Data; import java.util.Map; /** - * 管理后台 - Iot 统计 Response VO + * 管理后台 - IoT 统计 Response VO */ -@Schema(description = "管理后台 - Iot 统计 Response VO") +@Schema(description = "管理后台 - IoT 统计 Response VO") @Data public class IotStatisticsSummaryRespVO { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java index 89b162db91..2afad898b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelParam.java @@ -11,7 +11,7 @@ import lombok.Data; import java.util.List; /** - * IOT 产品物模型中的参数 + * IoT 产品物模型中的参数 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java index 447eb6e9ae..8064b10e5a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelPageReqVO.java @@ -6,13 +6,9 @@ import cn.iocoder.yudao.module.iot.enums.thingmodel.IotThingModelTypeEnum; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; @Schema(description = "管理后台 - IoT 产品物模型分页 Request VO") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) public class IotThingModelPageReqVO extends PageParam { @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java index 4c52031fe6..9633d2febe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java @@ -23,8 +23,6 @@ import java.util.Set; @TableName(value = "iot_device", autoResultMap = true) @KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java index 44c471216d..7865a44249 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java @@ -14,8 +14,6 @@ import lombok.*; @TableName("iot_device_group") @KeySequence("iot_device_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java index af5d8ac359..fa56f6938e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaFirmwareDO.java @@ -16,8 +16,6 @@ import lombok.*; @TableName(value = "iot_ota_firmware", autoResultMap = true) @KeySequence("iot_ota_firmware_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java index 52f6d83759..ff4f0e7a09 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeRecordDO.java @@ -17,8 +17,6 @@ import java.time.LocalDateTime; @TableName(value = "iot_ota_upgrade_record", autoResultMap = true) @KeySequence("iot_ota_upgrade_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java index 6cc80e2b76..221bdc56cd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/ota/IotOtaUpgradeTaskDO.java @@ -18,8 +18,6 @@ import java.util.List; @TableName(value = "iot_ota_upgrade_task", autoResultMap = true) @KeySequence("iot_ota_upgrade_task_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java index 69b7383910..cb247fc30b 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginConfigDO.java @@ -17,8 +17,6 @@ import lombok.*; @TableName("iot_plugin_config") @KeySequence("iot_plugin_config_seq") @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java index c64fe86c60..34abe893e8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInstanceDO.java @@ -16,8 +16,6 @@ import java.time.LocalDateTime; @TableName("iot_plugin_instance") @KeySequence("iot_plugin_instance_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java index a6510488ce..174342afb1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductCategoryDO.java @@ -14,8 +14,6 @@ import lombok.*; @TableName("iot_product_category") @KeySequence("iot_product_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java index 37c7be44be..3caebbccb8 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java @@ -14,8 +14,6 @@ import lombok.*; @TableName("iot_product") @KeySequence("iot_product_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java index 94734a2d38..c6a2390ac3 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertConfig.java @@ -20,8 +20,6 @@ import java.util.List; @TableName("iot_alert_config") @KeySequence("iot_alert_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java index fbcf1fe79d..840111078c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotAlertRecordDO.java @@ -18,8 +18,6 @@ import lombok.*; @TableName("iot_alert_record") @KeySequence("iot_alert_record_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java index 5697007b3c..fed4298720 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotDataBridgeDO.java @@ -20,8 +20,6 @@ import lombok.*; @TableName(value = "iot_data_bridge", autoResultMap = true) @KeySequence("iot_data_bridge_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java index 3c6ae6288e..f50101a4ed 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/IotRuleSceneDO.java @@ -27,8 +27,6 @@ import java.util.Map; @TableName("iot_rule_scene") @KeySequence("iot_rule_scene_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java index 8591e1539c..5e5d8200f4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/ota/IotOtaUpgradeRecordMapper.java @@ -74,10 +74,10 @@ public interface IotOtaUpgradeRecordMapper extends BaseMapperX selectUpgradeRecordPage(IotOtaUpgradeRecordPageReqVO pageReqVO) { diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java index bd4d258f7a..d09dac72de 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/redis/RedisKeyConstants.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDevicePropertyDO; import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInstanceDO; /** - * Iot Redis Key 枚举类 + * IoT Redis Key 枚举类 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java index c3855fbfe6..11d5d96beb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/producer/device/IotDeviceProducer.java @@ -7,7 +7,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; /** - * Iot 设备相关消息的 Producer + * IoT 设备相关消息的 Producer * * @author alwayssuper * @since 2024/12/17 16:35 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java index a673b538ef..c7b921c044 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/action/IotRuleSceneAction.java @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.iot.mq.message.IotDeviceMessage; import javax.annotation.Nullable; /** - * IOT 规则场景的场景执行器接口 + * IoT 规则场景的场景执行器接口 * * @author 芋道源码 */ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java index 08b6149c33..e31f40dd8d 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.plugin.common.pojo; import lombok.Data; import lombok.experimental.Accessors; -// TODO @芋艿:1)后续考虑,要不要叫 Iot 网关之类的 Response;2)包名 pojo +// TODO @芋艿:1)后续考虑,要不要叫 IoT 网关之类的 Response;2)包名 pojo /** * IoT 标准协议响应实体类 *

diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index 17403fef85..efd53c84a5 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -46,11 +46,11 @@ - - cn.iocoder.boot - yudao-module-bpm-biz - ${revision} - + + + + + @@ -109,11 +109,11 @@ - - cn.iocoder.boot - yudao-module-iot-biz - ${revision} - + + + + + diff --git a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java index f0e3878334..a9ed8fbf4c 100644 --- a/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java +++ b/yudao-server/src/main/java/cn/iocoder/yudao/server/controller/DefaultController.java @@ -73,7 +73,7 @@ public class DefaultController { @RequestMapping(value = { "/admin-api/iot/**"}) public CommonResult iot404() { return CommonResult.error(NOT_IMPLEMENTED.getCode(), - "[IOT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]"); + "[IoT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]"); } /** diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index b4447b07fb..c98a3277bb 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -66,7 +66,7 @@ spring: url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true username: root password: 123456 - tdengine: # IOT 数据库 + tdengine: # IoT 数据库 lazy: true # 开启懒加载,保证启动速度 url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro driver-class-name: com.taosdata.jdbc.rs.RestfulDriver From b6c7937aebe7d0d7b216e7735eea543e97e1c683 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 22:40:41 +0800 Subject: [PATCH 300/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9AOTA=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rule/vo/databridge/IotDataBridgePageReqVO.java | 5 ++++- .../admin/rule/vo/databridge/IotDataBridgeRespVO.java | 3 +-- .../databridge/config/IotDataBridgeAbstractConfig.java | 2 +- .../vo/databridge/config/IotDataBridgeHttpConfig.java | 2 +- .../databridge/config/IotDataBridgeKafkaMQConfig.java | 2 +- .../vo/databridge/config/IotDataBridgeMqttConfig.java | 2 +- .../databridge/config/IotDataBridgeRabbitMQConfig.java | 2 +- .../config/IotDataBridgeRedisStreamMQConfig.java | 3 ++- .../databridge/config/IotDataBridgeRocketMQConfig.java | 2 +- .../iot/controller/admin/rule/vo/package-info.java | 1 + .../admin/statistics/IotStatisticsController.java | 6 ++++-- .../admin/thingmodel/model/ThingModelEvent.java | 2 +- .../admin/thingmodel/model/ThingModelProperty.java | 2 +- .../admin/thingmodel/model/ThingModelService.java | 2 +- .../model/dataType/ThingModelArrayDataSpecs.java | 10 +++++----- .../model/dataType/ThingModelBoolOrEnumDataSpecs.java | 9 +++++---- .../thingmodel/model/dataType/ThingModelDataSpecs.java | 2 +- .../model/dataType/ThingModelDateOrTextDataSpecs.java | 4 ++-- .../model/dataType/ThingModelNumericDataSpec.java | 4 ++-- .../model/dataType/ThingModelStructDataSpecs.java | 4 ++-- .../iot/service/device/data/IotDeviceLogService.java | 1 + 21 files changed, 39 insertions(+), 31 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java index 7228b23bb9..e4dc36ef9e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -16,7 +18,8 @@ public class IotDataBridgePageReqVO extends PageParam { @Schema(description = "桥梁名称", example = "赵六") private String name; - @Schema(description = "桥梁状态", example = "2") + @Schema(description = "桥梁状态", example = "1") + @InEnum(CommonStatusEnum.class) private Integer status; @Schema(description = "创建时间") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java index 79c2c5cbed..38e04b2ebe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge; import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -20,7 +19,7 @@ public class IotDataBridgeRespVO { @Schema(description = "桥梁描述", example = "随便") private String description; - @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer status; @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java index 16481193f2..527e79b351 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; /** - * 抽象类 IotDataBridgeConfig + * IoT IotDataBridgeConfig 抽象类 * * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java index 9711cb6ec9..eca35c76ec 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java @@ -5,7 +5,7 @@ import lombok.Data; import java.util.Map; /** - * HTTP 配置 + * IoT HTTP 配置 {@link IotDataBridgeAbstractConfig} 实现类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java index cbc37cd691..1201214d12 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; import lombok.Data; /** - * Kafka 配置 + * IoT Kafka 配置 {@link IotDataBridgeAbstractConfig} 实现类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java index c437898c23..448b21501d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; import lombok.Data; /** - * MQTT 配置 + * IoT MQTT 配置 {@link IotDataBridgeAbstractConfig} 实现类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java index 40e72f14c9..2c247d1d58 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; import lombok.Data; /** - * RabbitMQ 配置 + * IoT RabbitMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java index 288b772a02..3c9bb330fe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java @@ -2,8 +2,9 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; import lombok.Data; +// TODO @puhui999:MQ 可以去掉哈。stream 更精准 /** - * Redis Stream MQ 配置 + * IoT Redis Stream 配置 {@link IotDataBridgeAbstractConfig} 实现类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java index 791d362508..e23e3061a1 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config; import lombok.Data; /** - * RocketMQ 配置 + * IoT RocketMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java index a977d86e93..f397e0acdb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java @@ -1 +1,2 @@ +// TODO @芋艿:占位 package cn.iocoder.yudao.module.iot.controller.admin.rule.vo; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java index 31cc5121ad..a9c195656c 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java @@ -21,6 +21,8 @@ import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; import java.util.Map; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.*; + @Tag(name = "管理后台 - IoT 数据统计") @RestController @RequestMapping("/iot/statistics") @@ -61,7 +63,7 @@ public class IotStatisticsController { respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L)); respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L)); respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L)); - return CommonResult.success(respVO); + return success(respVO); } // TODO @super:要不干掉 IotStatisticsReqVO 参数,直接使用 @RequestParam 接收,简单一些。 @@ -69,7 +71,7 @@ public class IotStatisticsController { @Operation(summary = "获取 IoT 设备上下行消息数据统计") public CommonResult getIotStatisticsDeviceMessageSummary( @Valid IotStatisticsReqVO reqVO) { - return CommonResult.success(new IotStatisticsDeviceMessageSummaryRespVO() + return success(new IotStatisticsDeviceMessageSummaryRespVO() .setDownstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())) .setDownstreamCounts((deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime())))); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java index d3f0d33c5c..06cc43809e 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java @@ -10,7 +10,7 @@ import lombok.Data; import java.util.List; /** - * 物模型中的事件 + * IoT 物模型中的事件 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java index 395c8611e4..4b9a05a0e2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelProperty.java @@ -11,7 +11,7 @@ import lombok.Data; import java.util.List; /** - * 物模型中的属性 + * IoT 物模型中的属性 * * dataSpecs 和 dataSpecsList 之中必须传入且只能传入一个 * diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java index 7f97b94029..c98acd8243 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java @@ -10,7 +10,7 @@ import lombok.Data; import java.util.List; /** - * 物模型中的服务 + * IoT 物模型中的服务 * * @author HUIHUI */ diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java index b8b1a29753..50011aabf4 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArrayDataSpecs.java @@ -7,25 +7,25 @@ import lombok.EqualsAndHashCode; import java.util.List; /** - * 物模型数据类型为数组的 DataSpec 定义 + * IoT 物模型数据类型为数组的 DataSpec 定义 * * @author HUIHUI */ @Data @EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 public class ThingModelArrayDataSpecs extends ThingModelDataSpecs { /** - * 数组中的元素个数。 + * 数组中的元素个数 */ private Integer size; /** - * 数组中的元素的数据类型。可选值:struct、int、float、double 或 text。 + * 数组中的元素的数据类型。可选值:struct、int、float、double 或 text */ private String childDataType; /** - * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中。 + * 数据类型(childDataType)为列表型 struct 的数据规范存储在 dataSpecsList 中 * 此时 struct 取值范围为:int、float、double、text、date、enum、bool */ private List dataSpecsList; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java index 3ab624cabb..925bc67196 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelBoolOrEnumDataSpecs.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 物模型数据类型为布尔型或枚举型的 DataSpec 定义 + * IoT 物模型数据类型为布尔型或枚举型的 DataSpec 定义 * * 数据类型,取值为 bool 或 enum。 * @@ -13,13 +13,14 @@ import lombok.EqualsAndHashCode; */ @Data @EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 public class ThingModelBoolOrEnumDataSpecs extends ThingModelDataSpecs { + // TODO @puhui999:要不写下参数校验?这样,注释可以简洁一点 /** * 枚举项的名称。 - * 可包含中文、大小写英文字母、数字、下划线(_)和短划线(-)。 - * 必须以中文、英文字母或数字开头,长度不超过 20 个字符。 + * 可包含中文、大小写英文字母、数字、下划线(_)和短划线(-) + * 必须以中文、英文字母或数字开头,长度不超过 20 个字符 */ private String name; /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java index 78bfd02dd2..d9fc12dd95 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDataSpecs.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; /** - * 抽象类 ThingModelDataSpecs + * IoT ThingModelDataSpecs 抽象类 * * 用于表示物模型数据的通用类型,根据具体的 "dataType" 字段动态映射到对应的子类。 * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java index 9d2c88ae12..62500bc560 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelDateOrTextDataSpecs.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 物模型数据类型为时间型或文本型的 DataSpec 定义 + * IoT 物模型数据类型为时间型或文本型的 DataSpec 定义 * * 数据类型,取值为 date 或 text。 * @@ -13,7 +13,7 @@ import lombok.EqualsAndHashCode; */ @Data @EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 public class ThingModelDateOrTextDataSpecs extends ThingModelDataSpecs { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java index b65d606ac9..8d0827c011 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelNumericDataSpec.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; /** - * 物模型数据类型为数值的 DataSpec 定义 + * IoT 物模型数据类型为数值的 DataSpec 定义 * * 数据类型,取值为 int、float 或 double。 * @@ -13,7 +13,7 @@ import lombok.EqualsAndHashCode; */ @Data @EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 public class ThingModelNumericDataSpec extends ThingModelDataSpecs { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java index 8fcaefdc5f..6d483eeaa9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelStructDataSpecs.java @@ -8,13 +8,13 @@ import lombok.EqualsAndHashCode; import java.util.List; /** - * 物模型数据类型为 struct 的 DataSpec 定义 + * IoT 物模型数据类型为 struct 的 DataSpec 定义 * * @author HUIHUI */ @Data @EqualsAndHashCode(callSuper = true) -@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复。 +@JsonIgnoreProperties({"dataType"}) // 忽略子类中的 dataType 字段,从而避免重复 public class ThingModelStructDataSpecs extends ThingModelDataSpecs { /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java index 468599e1ce..b79732911d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogService.java @@ -47,6 +47,7 @@ public interface IotDeviceLogService { */ Long getDeviceLogCount(@Nullable LocalDateTime createTime); + // TODO @super:deviceKey 是不是用不上哈? /** * 获得每个小时设备上行消息数量统计 * From a9733b4d2af6caf65445ce2ce4aac3295c106618 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 23:11:04 +0800 Subject: [PATCH 301/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B4=E4=BD=93=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotPluginCommonAutoConfiguration.java | 5 +- .../IotDeviceConfigSetVertxHandler.java | 10 ++- .../IotDeviceOtaUpgradeVertxHandler.java | 11 ++-- .../common/pojo/IotStandardResponse.java | 3 +- .../common/util/IotPluginCommonUtils.java | 1 + .../yudao-module-iot-plugin-emqx/pom.xml | 1 + .../IotPluginEmqxAutoConfiguration.java | 4 +- .../emqx/config/IotPluginEmqxProperties.java | 9 +-- .../IotDeviceDownstreamHandlerImpl.java | 9 ++- .../upstream/IotDeviceUpstreamServer.java | 62 +++++++++---------- .../router/IotDeviceAuthVertxHandler.java | 3 +- .../router/IotDeviceMqttMessageHandler.java | 2 +- .../router/IotDeviceWebhookVertxHandler.java | 5 +- .../http/config/IotHttpVertxPlugin.java | 4 +- .../router/IotDeviceUpstreamVertxHandler.java | 35 ++++++----- .../yudao-module-iot-plugin-mqtt/pom.xml | 1 + .../yudao/module/iot/plugin/MqttPlugin.java | 1 + .../iot/plugin/MqttServerExtension.java | 1 + 18 files changed, 83 insertions(+), 84 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java index 111189875d..ba7d56fe61 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/IotPluginCommonAutoConfiguration.java @@ -43,8 +43,9 @@ public class IotPluginCommonAutoConfiguration { } @Bean(initMethod = "init", destroyMethod = "stop") - public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob( - IotDeviceUpstreamApi deviceDataApi, IotDeviceDownstreamServer deviceDownstreamServer, IotPluginCommonProperties commonProperties) { + public IotPluginInstanceHeartbeatJob pluginInstanceHeartbeatJob(IotDeviceUpstreamApi deviceDataApi, + IotDeviceDownstreamServer deviceDownstreamServer, + IotPluginCommonProperties commonProperties) { return new IotPluginInstanceHeartbeatJob(deviceDataApi, deviceDownstreamServer, commonProperties); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java index b9bd4a52f1..1693f128d6 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceConfigSetVertxHandler.java @@ -25,6 +25,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC @RequiredArgsConstructor public class IotDeviceConfigSetVertxHandler implements Handler { + // TODO @haohao:是不是可以把 PATH、Method 所有的,抽到一个枚举类里?因为 topic、path、method 相当于不同的几个表达? public static final String PATH = "/sys/:productKey/:deviceName/thing/service/config/set"; public static final String METHOD = "thing.service.config.set"; @@ -57,12 +58,9 @@ public class IotDeviceConfigSetVertxHandler implements Handler { CommonResult result = deviceDownstreamHandler.setDeviceConfig(reqDTO); // 3. 响应结果 - IotStandardResponse response; - if (result.isSuccess()) { - response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); - } else { - response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); - } + IotStandardResponse response = result.isSuccess() ? + IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()) + : IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) 配置设置异常]", reqDTO, e); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java index a49b84acca..b417229aae 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/downstream/router/IotDeviceOtaUpgradeVertxHandler.java @@ -62,15 +62,14 @@ public class IotDeviceOtaUpgradeVertxHandler implements Handler CommonResult result = deviceDownstreamHandler.upgradeDeviceOta(reqDTO); // 3. 响应结果 - IotStandardResponse response; - if (result.isSuccess()) { - response = IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()); - } else { - response = IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); - } + // TODO @haohao:可以考虑 IotStandardResponse.of(requestId, method, CommonResult) + IotStandardResponse response = result.isSuccess() ? + IotStandardResponse.success(reqDTO.getRequestId(), METHOD, result.getData()) + :IotStandardResponse.error(reqDTO.getRequestId(), METHOD, result.getCode(), result.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][请求参数({}) OTA 升级异常]", reqDTO, e); + // TODO @haohao:可以考虑 IotStandardResponse.of(requestId, method, ErrorCode) IotStandardResponse errorResponse = IotStandardResponse.error( reqDTO.getRequestId(), METHOD, INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg()); IotPluginCommonUtils.writeJsonResponse(routingContext, errorResponse); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java index e31f40dd8d..131eb1b9ce 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/pojo/IotStandardResponse.java @@ -1,7 +1,6 @@ package cn.iocoder.yudao.module.iot.plugin.common.pojo; import lombok.Data; -import lombok.experimental.Accessors; // TODO @芋艿:1)后续考虑,要不要叫 IoT 网关之类的 Response;2)包名 pojo /** @@ -12,7 +11,6 @@ import lombok.experimental.Accessors; * @author haohao */ @Data -@Accessors(chain = true) public class IotStandardResponse { /** @@ -92,4 +90,5 @@ public class IotStandardResponse { .setMethod(method) .setVersion("1.0"); } + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java index 2e09c3c5c3..34c6c0fe2b 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/util/IotPluginCommonUtils.java @@ -72,4 +72,5 @@ public class IotPluginCommonUtils { .putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE) .end(JsonUtils.toJsonString(response)); } + } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml index 818c08b333..8620ecaa65 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/pom.xml @@ -15,6 +15,7 @@ 1.0.0 ${project.artifactId} + 物联网 插件模块 - emqx 插件 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java index 382bb9ecf2..e1d11504cf 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxAutoConfiguration.java @@ -14,7 +14,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * IoT 插件 Emqx 的专用自动配置类 + * IoT 插件 EMQX 的专用自动配置类 * * @author haohao */ @@ -34,7 +34,7 @@ public class IotPluginEmqxAutoConfiguration { .setClientId("yudao-iot-downstream-" + IdUtil.fastSimpleUUID()) .setUsername(emqxProperties.getMqttUsername()) .setPassword(emqxProperties.getMqttPassword()) - .setSsl(emqxProperties.isMqttSsl()); + .setSsl(emqxProperties.getMqttSsl()); return MqttClient.create(vertx, options); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java index 4117e71820..219fe0360f 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/config/IotPluginEmqxProperties.java @@ -14,6 +14,8 @@ import org.springframework.validation.annotation.Validated; @Data public class IotPluginEmqxProperties { + // TODO @haohao:参数校验,加下,啊哈 + /** * 服务主机 */ @@ -21,12 +23,11 @@ public class IotPluginEmqxProperties { /** * 服务端口 */ - private int mqttPort; + private Integer mqttPort; /** * 服务用户名 */ private String mqttUsername; - /** * 服务密码 */ @@ -34,7 +35,7 @@ public class IotPluginEmqxProperties { /** * 是否启用 SSL */ - private boolean mqttSsl; + private Boolean mqttSsl; /** * 订阅的主题列表 @@ -44,6 +45,6 @@ public class IotPluginEmqxProperties { /** * 认证端口 */ - private int authPort; + private Integer authPort; } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java index c1e64afb97..f5c19224af 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/downstream/IotDeviceDownstreamHandlerImpl.java @@ -25,6 +25,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle private static final String SYS_TOPIC_PREFIX = "/sys/"; + // TODO @haohao:是不是可以类似 IotDeviceConfigSetVertxHandler 的建议,抽到统一的枚举类 // TODO @haohao:讨论,感觉 mqtt 和 http,可以做个相对统一的格式哈。;回复 都使用 Alink 格式,方便后续扩展。 // 设备服务调用 标准 JSON // 请求Topic:/sys/${productKey}/${deviceName}/thing/service/${tsl.service.identifier} @@ -63,7 +64,6 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle // 构建请求消息 String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); JSONObject request = buildServiceRequest(requestId, reqDTO.getIdentifier(), reqDTO.getParams()); - // 发送消息 publishMessage(topic, request); @@ -82,9 +82,8 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle @Override public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO reqDTO) { - log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); - // 验证参数 + log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null) { log.error("[setProperty][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO)); return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg()); @@ -96,7 +95,6 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle // 构建请求消息 String requestId = reqDTO.getRequestId() != null ? reqDTO.getRequestId() : generateRequestId(); JSONObject request = buildPropertySetRequest(requestId, reqDTO.getProperties()); - // 发送消息 publishMessage(topic, request); @@ -132,6 +130,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle return SYS_TOPIC_PREFIX + productKey + "/" + deviceName + PROPERTY_SET_TOPIC; } + // TODO @haohao:这个,后面搞个对象,会不会好点哈? /** * 构建服务调用请求 */ @@ -168,7 +167,7 @@ public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandle } /** - * 生成请求ID + * 生成请求 ID */ private String generateRequestId() { return IdUtil.fastSimpleUUID(); diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index 040985ba9a..8911a76a80 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -36,10 +36,6 @@ public class IotDeviceUpstreamServer { * 连接超时时间(毫秒) */ private static final int CONNECTION_TIMEOUT_MS = 10000; - /** - * 主题分隔符 - */ - private static final String TOPIC_SEPARATOR = ","; /** * 默认 QoS 级别 */ @@ -84,42 +80,40 @@ public class IotDeviceUpstreamServer { */ public void start() { if (isRunning) { - log.warn("服务已经在运行中,请勿重复启动"); + log.warn("[start][服务已经在运行中,请勿重复启动]"); return; } - - log.info("[start] 开始启动服务"); + log.info("[start][开始启动服务]"); // 1. 启动 HTTP 服务器 CompletableFuture httpFuture = server.listen(emqxProperties.getAuthPort()) .toCompletionStage() .toCompletableFuture() - .thenAccept(v -> log.info("[start] HTTP服务器启动完成,端口: {}", server.actualPort())); + .thenAccept(v -> log.info("[start][HTTP服务器启动完成,端口: {}]", server.actualPort())); // 2. 连接 MQTT Broker CompletableFuture mqttFuture = connectMqtt() .toCompletionStage() .toCompletableFuture() .thenAccept(v -> { - // 3. 添加 MQTT 断开重连监听器 + // 2.1 添加 MQTT 断开重连监听器 client.closeHandler(closeEvent -> { - log.warn("[closeHandler] MQTT连接已断开,准备重连"); + log.warn("[closeHandler][MQTT连接已断开,准备重连]"); reconnectWithDelay(); }); - - // 4. 设置 MQTT 消息处理器 + // 2. 设置 MQTT 消息处理器 setupMessageHandler(); }); - // 等待所有服务启动完成 + // 3. 等待所有服务启动完成 CompletableFuture.allOf(httpFuture, mqttFuture) .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) .whenComplete((result, error) -> { if (error != null) { - log.error("[start] 服务启动失败", error); + log.error("[start][服务启动失败]", error); } else { isRunning = true; - log.info("[start] 所有服务启动完成"); + log.info("[start][所有服务启动完成]"); } }); } @@ -129,7 +123,7 @@ public class IotDeviceUpstreamServer { */ private void setupMessageHandler() { client.publishHandler(mqttMessageHandler::handle); - log.debug("[setupMessageHandler] MQTT消息处理器设置完成"); + log.debug("[setupMessageHandler][MQTT消息处理器设置完成]"); } /** @@ -137,12 +131,12 @@ public class IotDeviceUpstreamServer { */ private void reconnectWithDelay() { if (!isRunning) { - log.info("[reconnectWithDelay] 服务已停止,不再尝试重连"); + log.info("[reconnectWithDelay][服务已停止,不再尝试重连]"); return; } vertx.setTimer(RECONNECT_DELAY_MS, id -> { - log.info("[reconnectWithDelay] 开始重新连接MQTT"); + log.info("[reconnectWithDelay][开始重新连接 MQTT]"); connectMqtt(); }); } @@ -155,28 +149,28 @@ public class IotDeviceUpstreamServer { private Future connectMqtt() { return client.connect(emqxProperties.getMqttPort(), emqxProperties.getMqttHost()) .compose(connAck -> { - log.info("[connectMqtt] MQTT客户端连接成功"); + log.info("[connectMqtt][MQTT客户端连接成功]"); return subscribeToTopics(); }) - .recover(err -> { - log.error("[connectMqtt] 连接MQTT Broker失败: {}", err.getMessage()); + .recover(error -> { + log.error("[connectMqtt][连接MQTT Broker失败:]", error); reconnectWithDelay(); - return Future.failedFuture(err); + return Future.failedFuture(error); }); } /** * 订阅设备上行消息主题 * - * @return 订阅结果的Future + * @return 订阅结果的 Future */ private Future subscribeToTopics() { String[] topics = emqxProperties.getMqttTopics(); if (ArrayUtil.isEmpty(topics)) { - log.warn("[subscribeToTopics] 未配置MQTT主题,跳过订阅"); + log.warn("[subscribeToTopics][未配置MQTT主题,跳过订阅]"); return Future.succeededFuture(); } - log.info("[subscribeToTopics] 开始订阅设备上行消息主题"); + log.info("[subscribeToTopics][开始订阅设备上行消息主题]"); Future compositeFuture = Future.succeededFuture(); for (String topic : topics) { @@ -186,11 +180,11 @@ public class IotDeviceUpstreamServer { } compositeFuture = compositeFuture.compose(v -> client.subscribe(trimmedTopic, DEFAULT_QOS.value()) .map(ack -> { - log.info("[subscribeToTopics] 成功订阅主题: {}", trimmedTopic); + log.info("[subscribeToTopics][成功订阅主题: {}]", trimmedTopic); return null; }) - .recover(err -> { - log.error("[subscribeToTopics] 订阅主题失败: {}, 原因: {}", trimmedTopic, err.getMessage()); + .recover(error -> { + log.error("[subscribeToTopics][订阅主题失败: {}]", trimmedTopic, error); return Future.succeededFuture(); // 继续订阅其他主题 })); } @@ -202,10 +196,10 @@ public class IotDeviceUpstreamServer { */ public void stop() { if (!isRunning) { - log.warn("[stop] 服务未运行,无需停止"); + log.warn("[stop][服务未运行,无需停止]"); return; } - log.info("[stop] 开始关闭服务"); + log.info("[stop][开始关闭服务]"); isRunning = false; try { @@ -224,14 +218,14 @@ public class IotDeviceUpstreamServer { .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) .whenComplete((result, error) -> { if (error != null) { - log.error("[stop] 服务关闭过程中发生异常", error); + log.error("[stop][服务关闭过程中发生异常]", error); } else { - log.info("[stop] 所有服务关闭完成"); + log.info("[stop][所有服务关闭完成]"); } }); } catch (Exception e) { - log.error("[stop] 关闭服务异常", e); - throw new RuntimeException("关闭IoT设备上行服务失败", e); + log.error("[stop][关闭服务异常]", e); + throw new RuntimeException("关闭 IoT 设备上行服务失败", e); } } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java index fcb2286158..e9206d5b64 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceAuthVertxHandler.java @@ -15,7 +15,7 @@ import java.util.Collections; /** * IoT EMQX 连接认证的 Vert.x Handler * - * EMQX HTTP + * 参考:EMQX HTTP * * 注意:该处理器需要返回特定格式:{"result": "allow"} 或 {"result": "deny"}, * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类 @@ -31,7 +31,6 @@ public class IotDeviceAuthVertxHandler implements Handler { private final IotDeviceUpstreamApi deviceUpstreamApi; @Override - @SuppressWarnings("unchecked") public void handle(RoutingContext routingContext) { try { // 构建认证请求 DTO diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java index 6cf8d84c5c..00fa1b96d7 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceMqttMessageHandler.java @@ -23,7 +23,7 @@ import java.util.Map; /** * IoT 设备 MQTT 消息处理器 * - * 参考:"设备属性、事件、服务"> + * 参考:设备属性、事件、服务 */ @Slf4j public class IotDeviceMqttMessageHandler { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java index 93fb01bc0a..21b49e097c 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/router/IotDeviceWebhookVertxHandler.java @@ -18,7 +18,7 @@ import java.util.Collections; /** * IoT EMQX Webhook 事件处理的 Vert.x Handler * - * EMQX Webhook + * 参考:EMQX Webhook * * 注意:该处理器需要返回特定格式:{"result": "success"} 或 {"result": "error"}, * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。 @@ -51,8 +51,7 @@ public class IotDeviceWebhookVertxHandler implements Handler { handleClientDisconnected(clientId, username); break; default: - log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, - username); + log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, username); break; } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java index 674980d005..f704c18443 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/IotHttpVertxPlugin.java @@ -23,10 +23,8 @@ public class IotHttpVertxPlugin extends SpringPlugin { public void start() { log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动开始...]"); try { - // 1. 获取插件上下文 ApplicationContext pluginContext = getApplicationContext(); Assert.notNull(pluginContext, "pluginContext 不能为空"); - log.info("[HttpVertxPlugin][HttpVertxPlugin 插件启动成功...]"); } catch (Exception e) { log.error("[HttpVertxPlugin][HttpVertxPlugin 插件开启动异常...]", e); @@ -43,6 +41,7 @@ public class IotHttpVertxPlugin extends SpringPlugin { } } + // TODO @芋艿:思考下,未来要不要。。。 @Override protected ApplicationContext createApplicationContext() { // 创建插件自己的 ApplicationContext @@ -52,6 +51,7 @@ public class IotHttpVertxPlugin extends SpringPlugin { // 继续使用插件自己的 ClassLoader 以加载插件内部的类 pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); // 扫描当前插件的自动配置包 + // TODO @芋艿:后续看看,怎么配置类包 pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); pluginContext.refresh(); return pluginContext; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java index f6c7cc3a27..79d465ea03 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/upstream/router/IotDeviceUpstreamVertxHandler.java @@ -34,6 +34,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC @Slf4j public class IotDeviceUpstreamVertxHandler implements Handler { + // TODO @haohao:要不要类似 IotDeviceConfigSetVertxHandler 写的,把这些 PATH、METHOD 之类的抽走 /** * 属性上报路径 */ @@ -49,6 +50,7 @@ public class IotDeviceUpstreamVertxHandler implements Handler { private final IotDeviceUpstreamApi deviceUpstreamApi; + // TODO @haohao:要不要分成多个 Handler?每个只解决一个问题哈。 @Override public void handle(RoutingContext routingContext) { String path = routingContext.request().path(); @@ -102,7 +104,6 @@ public class IotDeviceUpstreamVertxHandler implements Handler { IotPluginCommonUtils.writeJsonResponse(routingContext, response); } catch (Exception e) { log.error("[handle][处理上行请求异常] path={}", path, e); - // 构建错误响应 String method = path.contains("/property/") ? PROPERTY_METHOD : EVENT_METHOD_PREFIX + (routingContext.pathParams().containsKey("identifier") ? routingContext.pathParam("identifier") @@ -115,27 +116,28 @@ public class IotDeviceUpstreamVertxHandler implements Handler { /** * 更新设备状态 * - * @param productKey 产品Key + * @param productKey 产品 Key * @param deviceName 设备名称 */ private void updateDeviceState(String productKey, String deviceName) { - deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState())); + deviceUpstreamApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) new IotDeviceStateUpdateReqDTO() + .setRequestId(IdUtil.fastSimpleUUID()).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)).setState(IotDeviceStateEnum.ONLINE.getState())); } /** * 解析属性上报请求 * - * @param productKey 产品Key + * @param productKey 产品 Key * @param deviceName 设备名称 - * @param requestId 请求ID + * @param requestId 请求 ID * @param body 请求体 - * @return 属性上报请求DTO + * @return 属性上报请求 DTO */ @SuppressWarnings("unchecked") private IotDevicePropertyReportReqDTO parsePropertyReportRequest(String productKey, String deviceName, String requestId, JsonObject body) { // 按照标准 JSON 格式处理属性数据 Map properties = new HashMap<>(); - // 优先使用 params 字段,符合标准 Map params = body.getJsonObject("params") != null ? body.getJsonObject("params").getMap() : null; if (params != null) { // 将标准格式的 params 转换为平台需要的 properties 格式 @@ -153,24 +155,25 @@ public class IotDeviceUpstreamVertxHandler implements Handler { } // 构建属性上报请求 DTO - return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); + return ((IotDevicePropertyReportReqDTO) new IotDevicePropertyReportReqDTO().setRequestId(requestId) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)).setProperties(properties); } /** * 解析事件上报请求 * - * @param productKey 产品Key + * @param productKey 产品K ey * @param deviceName 设备名称 * @param identifier 事件标识符 - * @param requestId 请求ID + * @param requestId 请求 ID * @param body 请求体 - * @return 事件上报请求DTO + * @return 事件上报请求 DTO */ private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier, String requestId, JsonObject body) { - // 按照标准JSON格式处理事件参数 + // 按照标准 JSON 格式处理事件参数 Map params; - // 优先使用params字段,符合标准 - if (body.getJsonObject("params") != null) { + if (body.containsKey("params")) { params = body.getJsonObject("params").getMap(); } else { // 兼容旧格式 @@ -178,6 +181,8 @@ public class IotDeviceUpstreamVertxHandler implements Handler { } // 构建事件上报请求 DTO - return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId).setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()).setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); + return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO().setRequestId(requestId) + .setProcessId(IotPluginCommonUtils.getProcessId()).setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)).setIdentifier(identifier).setParams(params); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml index e007596dc0..f1fba50590 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/pom.xml @@ -14,6 +14,7 @@ yudao-module-iot-plugin-mqtt ${project.artifactId} + 物联网 插件模块 - mqtt 插件 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java index 54ff31f36b..7883fa8b12 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttPlugin.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; import org.pf4j.PluginWrapper; +// TODO @芋艿:暂未实现 @Slf4j public class MqttPlugin extends Plugin { diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java index 868d238ee9..dd0c5da372 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-mqtt/src/main/java/cn/iocoder/yudao/module/iot/plugin/MqttServerExtension.java @@ -21,6 +21,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; +// TODO @芋艿:暂未实现 /** * 根据官方示例,整合常见 MQTT 功能到 PF4J 的 Extension 类中 */ From 638976dac81a7098b0e8d563379a05e7fd4d6c8b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 23:15:32 +0800 Subject: [PATCH 302/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B4=E4=BD=93=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../control/IotDeviceUpstreamServiceImpl.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index 29e8096a95..6c80e75ace 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -280,11 +280,11 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { sendDeviceMessage(message, device); } + // TODO @芋艿:后续需要考虑,http 的认证 @Override public boolean authenticateEmqxConnection(IotDeviceEmqxAuthReqDTO authReqDTO) { log.info("[authenticateEmqxConnection][认证 Emqx 连接: {}]", authReqDTO); - // 1. 校验设备是否存在 - // username 格式:${DeviceName}&${ProductKey} + // 1.1 校验设备是否存在。username 格式:${DeviceName}&${ProductKey} String[] usernameParts = authReqDTO.getUsername().split("&"); if (usernameParts.length != 2) { log.error("[authenticateEmqxConnection][认证失败,username 格式不正确]"); @@ -292,17 +292,19 @@ public class IotDeviceUpstreamServiceImpl implements IotDeviceUpstreamService { } String deviceName = usernameParts[0]; String productKey = usernameParts[1]; - IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache( - productKey, deviceName); + // 1.2 获得设备 + IotDeviceDO device = deviceService.getDeviceByProductKeyAndDeviceNameFromCache(productKey, deviceName); if (device == null) { - log.error("[authenticateEmqxConnection][设备({}/{}) 不存在]", - productKey, deviceName); + log.error("[authenticateEmqxConnection][设备({}/{}) 不存在]", productKey, deviceName); return false; } + // TODO @haohao:需要记录,记录设备的最后时间 + // 2. 校验密码 String deviceSecret = device.getDeviceSecret(); String clientId = authReqDTO.getClientId(); MqttSignResult sign = MqttSignUtils.calculate(productKey, deviceName, deviceSecret, clientId); + // TODO 建议,先失败,return false; if (StrUtil.equals(sign.getPassword(), authReqDTO.getPassword())) { log.info("[authenticateEmqxConnection][认证成功]"); return true; From 7c84ab9919c0c9765cb860996be70919debedcdc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 17 Mar 2025 13:14:54 +0800 Subject: [PATCH 303/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B4=E4=BD=93=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/mysql/ruoyi-vue-pro.sql | 323 ++++++++++++++---- .../core/client/impl/AliyunSmsClientTest.java | 7 + .../src/main/resources/application-local.yaml | 26 +- 3 files changed, 275 insertions(+), 81 deletions(-) diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index b1a07fc61c..bd9cd6d4ac 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80200 (8.2.0) File Encoding : 65001 - Date: 14/03/2025 22:52:31 + Date: 17/03/2025 13:14:16 */ SET NAMES utf8mb4; @@ -91,7 +91,7 @@ CREATE TABLE `infra_api_error_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 21417 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 21482 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; -- ---------------------------- -- Records of infra_api_error_log @@ -250,7 +250,7 @@ CREATE TABLE `infra_file` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1655 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; +) ENGINE = InnoDB AUTO_INCREMENT = 1657 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; -- ---------------------------- -- Records of infra_file @@ -444,7 +444,7 @@ CREATE TABLE `system_dict_data` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1694 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; +) ENGINE = InnoDB AUTO_INCREMENT = 3000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表'; -- ---------------------------- -- Records of system_dict_data @@ -870,30 +870,6 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1591, 4, '七牛云', 'QINIU', 'system_sms_channel_code', 0, '', '', '', '1', '2024-08-31 08:45:03', '1', '2024-08-31 08:45:24', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1592, 3, '新人券', '3', 'promotion_coupon_take_type', 0, 'info', '', '新人注册后,自动发放', '1', '2024-09-03 11:57:16', '1', '2024-09-03 11:57:28', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1593, 5, '微信零钱', '5', 'brokerage_withdraw_type', 0, '', '', '自动打款', '1', '2024-10-13 11:06:48', '1', '2024-10-13 11:06:59', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1655, 0, '标准数据格式(JSON)', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2024-09-06 14:31:02', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1656, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2024-09-06 14:30:54', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1657, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2024-09-06 21:57:01', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1658, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2024-09-06 21:56:46', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1659, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2024-09-06 21:57:10', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1661, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2024-09-06 22:06:22', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1663, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2024-09-07 10:58:07', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1665, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2024-09-06 22:02:44', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1666, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2024-09-06 22:02:51', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1667, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2024-09-06 22:04:47', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1668, 1, '蜂窝(2G / 3G / 4G / 5G)', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2024-09-06 22:05:14', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1669, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2024-09-06 22:05:35', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1670, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2024-09-06 22:05:52', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1671, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2024-09-06 22:26:10', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1672, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2024-09-06 22:26:21', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1673, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2024-09-06 22:26:31', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1674, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2024-09-06 22:26:39', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1675, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2024-09-06 22:26:48', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1676, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-01-28 16:09:27', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1677, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-01-28 16:09:26', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1678, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-01-28 16:09:25', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1680, 1, '属性', '1', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2024-09-29 20:09:41', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1681, 2, '服务', '2', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2024-09-29 20:08:23', b'0'); -INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1682, 3, '事件', '3', 'iot_product_function_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2024-09-29 20:08:20', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1683, 10, '字节豆包', 'DouBao', 'ai_platform', 0, '', '', '', '1', '2025-02-23 19:51:40', '1', '2025-02-23 19:52:02', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1684, 11, '腾讯混元', 'HunYuan', 'ai_platform', 0, '', '', '', '1', '2025-02-23 20:58:04', '1', '2025-02-23 20:58:04', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1685, 12, '硅基流动', 'SiliconFlow', 'ai_platform', 0, '', '', '', '1', '2025-02-24 20:19:09', '1', '2025-02-24 20:19:09', b'0'); @@ -905,6 +881,179 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1691, 6, '重排', '6', 'ai_model_type', 0, '', '', '', '1', '2025-03-03 12:28:26', '1', '2025-03-03 12:28:26', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1692, 14, 'MiniMax', 'MiniMax', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:04:51', '1', '2025-03-11 20:04:51', b'0'); INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1693, 15, '月之暗灭', 'Moonshot', 'ai_platform', 0, '', '', '', '1', '2025-03-11 20:05:08', '1', '2025-03-11 20:05:08', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2000, 0, '标准数据格式(JSON)', '0', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:26', '1', '2025-03-17 09:28:16', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2001, 1, '透传/自定义', '1', 'iot_data_format', 0, 'default', '', '', '1', '2024-08-10 11:53:37', '1', '2025-03-17 09:28:19', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2002, 0, '直连设备', '0', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:54:58', '1', '2025-03-17 09:28:22', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2003, 2, '网关设备', '2', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:08', '1', '2025-03-17 09:28:28', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2004, 1, '网关子设备', '1', 'iot_product_device_type', 0, 'default', '', '', '1', '2024-08-10 11:55:20', '1', '2025-03-17 09:28:31', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2005, 1, '已发布', '1', 'iot_product_status', 0, 'success', '', '', '1', '2024-08-10 12:10:33', '1', '2025-03-17 09:28:34', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2006, 0, '开发中', '0', 'iot_product_status', 0, 'default', '', '', '1', '2024-08-10 14:19:18', '1', '2025-03-17 09:28:39', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2007, 0, '弱校验', '0', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:05:48', '1', '2025-03-17 09:28:41', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2008, 1, '免校验', '1', 'iot_validate_type', 0, '', '', '', '1', '2024-09-06 20:06:03', '1', '2025-03-17 09:28:44', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2009, 0, 'Wi-Fi', '0', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:04:47', '1', '2025-03-17 09:28:47', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2010, 1, '蜂窝(2G / 3G / 4G / 5G)', '1', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:14', '1', '2025-03-17 09:28:49', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2011, 2, '以太网', '2', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:35', '1', '2025-03-17 09:28:51', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2012, 3, '其他', '3', 'iot_net_type', 0, '', '', '', '1', '2024-09-06 22:05:52', '1', '2025-03-17 09:28:54', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2013, 0, '自定义', '0', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:10', '1', '2025-03-17 09:28:56', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2014, 1, 'Modbus', '1', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:21', '1', '2025-03-17 09:28:58', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2015, 2, 'OPC UA', '2', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:31', '1', '2025-03-17 09:29:00', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2016, 3, 'ZigBee', '3', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:39', '1', '2025-03-17 09:29:04', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2017, 4, 'BLE', '4', 'iot_protocol_type', 0, '', '', '', '1', '2024-09-06 22:26:48', '1', '2025-03-17 09:29:06', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2018, 0, '未激活', '0', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:34', '1', '2025-03-17 09:29:09', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2019, 1, '在线', '1', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:48', '1', '2025-03-17 09:29:12', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2020, 2, '离线', '2', 'iot_device_state', 0, '', '', '', '1', '2024-09-21 08:13:59', '1', '2025-03-17 09:29:14', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2021, 1, '属性', '1', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:01', '1', '2025-03-17 09:29:24', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2022, 2, '服务', '2', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:11', '1', '2025-03-17 09:29:27', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2023, 3, '事件', '3', 'iot_thing_model_type', 0, '', '', '', '1', '2024-09-29 20:03:20', '1', '2025-03-17 09:29:29', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, 1, 'JAR 部署', '0', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:32', '1', '2025-03-17 09:29:32', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, 2, '独立部署', '1', 'iot_plugin_deploy_type', 0, '', '', '', '1', '2024-12-13 10:55:43', '1', '2025-03-17 09:29:34', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, 0, '停止', '0', 'iot_plugin_status', 0, 'danger', '', '', '1', '2024-12-13 11:07:37', '1', '2025-03-17 09:29:37', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2027, 1, '运行', '1', 'iot_plugin_status', 0, '', '', '', '1', '2024-12-13 11:07:45', '1', '2025-03-17 09:34:17', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2028, 0, '普通插件', '0', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:32', '1', '2025-03-17 09:34:19', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2029, 1, '设备插件', '1', 'iot_plugin_type', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:22', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2030, 1, '升每分钟', 'L/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:24', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2031, 2, '毫克每千克', 'mg/kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:27', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2032, 3, '浊度', 'NTU', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:31', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2033, 4, 'PH值', 'pH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:36', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2034, 5, '土壤EC值', 'dS/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:34:43', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2035, 6, '太阳总辐射', 'W/㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:20', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2036, 7, '降雨量', 'mm/hour', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:24', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2037, 8, '乏', 'var', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:27', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2038, 9, '厘泊', 'cP', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:36:33', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2039, 10, '饱和度', 'aw', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:11', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2040, 11, '个', 'pcs', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:19', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2041, 12, '厘斯', 'cst', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:22', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2042, 13, '巴', 'bar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:24', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2043, 14, '纳克每升', 'ppt', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:27', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2044, 15, '微克每升', 'ppb', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:31', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2045, 16, '微西每厘米', 'uS/cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:34', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2046, 17, '牛顿每库仑', 'N/C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:38', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2047, 18, '伏特每米', 'V/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:43', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2048, 19, '滴速', 'ml/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2049, 20, '毫米汞柱', 'mmHg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:48', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2050, 21, '血糖', 'mmol/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:37:54', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2051, 22, '毫米每秒', 'mm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:02', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2052, 23, '转每分钟', 'turn/m', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:07', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2053, 24, '次', 'count', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:09', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2054, 25, '档', 'gear', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:11', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2055, 26, '步', 'stepCount', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:13', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2056, 27, '标准立方米每小时', 'Nm3/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:15', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2057, 28, '千伏', 'kV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:20', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2058, 29, '千伏安', 'kVA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:38:24', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2060, 30, '千乏', 'kVar', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2061, 31, '微瓦每平方厘米', 'uw/cm2', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2062, 32, '只', '只', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2063, 33, '相对湿度', '%RH', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2064, 34, '立方米每秒', 'm³/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2065, 35, '公斤每秒', 'kg/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2066, 36, '转每分钟', 'r/min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2067, 37, '吨每小时', 't/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2068, 38, '千卡每小时', 'KCL/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2069, 39, '升每秒', 'L/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2070, 40, '兆帕', 'Mpa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2071, 41, '立方米每小时', 'm³/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2072, 42, '千乏时', 'kvarh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2073, 43, '微克每升', 'μg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2074, 44, '千卡路里', 'kcal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2075, 45, '吉字节', 'GB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2076, 46, '兆字节', 'MB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2077, 47, '千字节', 'KB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2078, 48, '字节', 'B', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2079, 49, '微克每平方分米每天', 'μg/(d㎡·d)', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2080, 50, '无', '', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2081, 51, '百万分率', 'ppm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2082, 52, '像素', 'pixel', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2083, 53, '照度', 'Lux', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2084, 54, '重力加速度', 'grav', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2085, 55, '分贝', 'dB', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2086, 56, '百分比', '%', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2087, 57, '流明', 'lm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2088, 58, '比特', 'bit', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2089, 59, '克每毫升', 'g/mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2090, 60, '克每升', 'g/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2091, 61, '毫克每升', 'mg/L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2092, 62, '微克每立方米', 'μg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2093, 63, '毫克每立方米', 'mg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2094, 64, '克每立方米', 'g/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2095, 65, '千克每立方米', 'kg/m³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2096, 66, '纳法', 'nF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2097, 67, '皮法', 'pF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2098, 68, '微法', 'μF', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2099, 69, '法拉', 'F', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2100, 70, '欧姆', 'Ω', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2101, 71, '微安', 'μA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2102, 72, '毫安', 'mA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2103, 73, '千安', 'kA', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2104, 74, '安培', 'A', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2105, 75, '毫伏', 'mV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2106, 76, '伏特', 'V', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2107, 77, '毫秒', 'ms', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2108, 78, '秒', 's', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2109, 79, '分钟', 'min', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2110, 80, '小时', 'h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2111, 81, '日', 'day', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2112, 82, '周', 'week', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2113, 83, '月', 'month', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2114, 84, '年', 'year', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2115, 85, '节', 'kn', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2116, 86, '千米每小时', 'km/h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2117, 87, '米每秒', 'm/s', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2118, 88, '秒', '″', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2119, 89, '分', '′', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2120, 90, '度', '°', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2121, 91, '弧度', 'rad', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2122, 92, '赫兹', 'Hz', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2123, 93, '微瓦', 'μW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2124, 94, '毫瓦', 'mW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2125, 95, '千瓦特', 'kW', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2126, 96, '瓦特', 'W', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2127, 97, '卡路里', 'cal', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2128, 98, '千瓦时', 'kW·h', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2129, 99, '瓦时', 'Wh', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2130, 100, '电子伏', 'eV', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2131, 101, '千焦', 'kJ', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2132, 102, '焦耳', 'J', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2133, 103, '华氏度', '℉', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2134, 104, '开尔文', 'K', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2135, 105, '吨', 't', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2136, 106, '摄氏度', '°C', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2137, 107, '毫帕', 'mPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2138, 108, '百帕', 'hPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2139, 109, '千帕', 'kPa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2140, 110, '帕斯卡', 'Pa', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2141, 111, '毫克', 'mg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2142, 112, '克', 'g', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2143, 113, '千克', 'kg', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2144, 114, '牛', 'N', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2145, 115, '毫升', 'mL', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2146, 116, '升', 'L', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2147, 117, '立方毫米', 'mm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2148, 118, '立方厘米', 'cm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2149, 119, '立方千米', 'km³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2150, 120, '立方米', 'm³', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2151, 121, '公顷', 'h㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2152, 122, '平方厘米', 'c㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2153, 123, '平方毫米', 'm㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2154, 124, '平方千米', 'k㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2155, 125, '平方米', '㎡', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2156, 126, '纳米', 'nm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2157, 127, '微米', 'μm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2158, 128, '毫米', 'mm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2159, 129, '厘米', 'cm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2160, 130, '分米', 'dm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, 131, '千米', 'km', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, 132, '米', 'm', 'iot_thing_model_unit', 0, '', '', '', '1', '2024-12-13 11:08:41', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2163, 1, '输入', '1', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:24', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2164, 2, '输出', '2', 'iot_data_bridge_direction_enum', 0, 'primary', '', '', '1', '2025-03-09 12:38:36', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2165, 1, 'HTTP', '1', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:39:54', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2166, 2, 'TCP', '2', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:06', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2167, 3, 'WEBSOCKET', '3', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:24', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2168, 10, 'MQTT', '10', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:40:37', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2169, 20, 'DATABASE', '20', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:05', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2170, 21, 'REDIS_STREAM', '21', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:18', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2171, 30, 'ROCKETMQ', '30', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:30', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2172, 31, 'RABBITMQ', '31', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:47', '1', '2025-03-17 09:40:46', b'0'); +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2173, 32, 'KAFKA', '32', 'iot_data_bridge_type_enum', 0, 'primary', '', '', '1', '2025-03-09 12:41:59', '1', '2025-03-17 09:40:46', b'0'); COMMIT; -- ---------------------------- @@ -924,7 +1073,7 @@ CREATE TABLE `system_dict_type` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 641 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; +) ENGINE = InnoDB AUTO_INCREMENT = 2000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表'; -- ---------------------------- -- Records of system_dict_type @@ -1023,15 +1172,21 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (627, '写作格式', 'ai_write_format', 0, '', '1', '2024-07-07 15:14:34', '1', '2024-07-07 15:14:34', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (628, 'AI 写作类型', 'ai_write_type', 0, '', '1', '2024-07-10 21:25:29', '1', '2024-07-10 21:25:29', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (629, 'BPM 流程模型类型', 'bpm_model_type', 0, '', '1', '2024-08-26 15:21:43', '1', '2024-08-26 15:21:43', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (630, 'IOT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2024-09-06 22:20:17', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (631, 'IOT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-01-28 16:09:42', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (632, 'IOT 物模型功能类型', 'iot_product_function_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2024-09-29 20:09:26', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (634, 'IOT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2024-09-06 14:30:14', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (635, 'IOT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2024-08-10 04:06:56', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (637, 'IOT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2024-08-10 12:06:09', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (638, 'IOT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2024-09-06 20:05:13', b'0', '1970-01-01 00:00:00'); -INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (639, 'IOT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2024-09-06 22:04:13', b'0', '1970-01-01 00:00:00'); INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (640, 'AI 模型类型', 'ai_model_type', 0, '', '1', '2025-03-03 12:24:07', '1', '2025-03-03 12:24:07', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1000, 'IoT 数据格式', 'iot_data_format', 0, '', '1', '2024-08-10 11:52:58', '1', '2025-03-17 09:25:06', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1001, 'IoT 产品设备类型', 'iot_product_device_type', 0, '', '1', '2024-08-10 11:54:30', '1', '2025-03-17 09:25:08', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1002, 'IoT 产品状态', 'iot_product_status', 0, '', '1', '2024-08-10 12:06:09', '1', '2025-03-17 09:25:10', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1003, 'IoT 数据校验级别', 'iot_validate_type', 0, '', '1', '2024-09-06 20:05:13', '1', '2025-03-17 09:25:12', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1004, 'IoT 联网方式', 'iot_net_type', 0, '', '1', '2024-09-06 22:04:13', '1', '2025-03-17 09:25:14', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1005, 'IoT 接入网关协议', 'iot_protocol_type', 0, '', '1', '2024-09-06 22:20:17', '1', '2025-03-17 09:25:16', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1006, 'IoT 设备状态', 'iot_device_state', 0, '', '1', '2024-09-21 08:12:55', '1', '2025-03-17 09:25:19', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1007, 'IoT 物模型功能类型', 'iot_thing_model_type', 0, '', '1', '2024-09-29 20:02:36', '1', '2025-03-17 09:25:24', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1008, 'IoT 插件部署方式', 'iot_plugin_deploy_type', 0, '', '1', '2024-12-13 10:55:13', '1', '2025-03-17 09:25:27', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1009, 'IoT 插件状态', 'iot_plugin_status', 0, '', '1', '2024-12-13 11:05:34', '1', '2025-03-17 09:25:30', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1010, 'IoT 插件类型', 'iot_plugin_type', 0, '', '1', '2024-12-13 11:08:19', '1', '2025-03-17 09:25:32', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1011, 'IoT 物模型单位', 'iot_thing_model_unit', 0, '', '1', '2024-12-25 17:36:46', '1', '2025-03-17 09:25:35', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1012, 'IoT 数据桥接的方向枚举', 'iot_data_bridge_direction_enum', 0, '', '1', '2025-03-09 12:37:40', '1', '2025-03-17 09:25:39', b'0', '1970-01-01 00:00:00'); +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1013, 'IoT 数据桥梁的类型枚举', 'iot_data_bridge_type_enum', 0, '', '1', '2025-03-09 12:39:36', '1', '2025-03-17 09:25:43', b'0', '1970-01-01 00:00:00'); COMMIT; -- ---------------------------- @@ -1055,7 +1210,7 @@ CREATE TABLE `system_login_log` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 3442 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; +) ENGINE = InnoDB AUTO_INCREMENT = 3446 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; -- ---------------------------- -- Records of system_login_log @@ -1186,16 +1341,16 @@ CREATE TABLE `system_menu` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 2925 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; +) ENGINE = InnoDB AUTO_INCREMENT = 5000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表'; -- ---------------------------- -- Records of system_menu -- ---------------------------- BEGIN; -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-06-18 01:19:41', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:27', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-03-01 08:28:40', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, 'OA 示例', '', 1, 40, 1185, 'oa', 'fa:road', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-09-20 16:26:19', '1', '2024-02-29 12:38:13', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:02:04', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (100, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2025-03-15 21:30:41', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (101, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-05-01 18:35:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (102, '菜单管理', '', 2, 3, 1, 'menu', 'ep:menu', 'system/menu/index', 'SystemMenu', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:03:50', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (103, '部门管理', '', 2, 4, 1, 'dept', 'fa:address-card', 'system/dept/index', 'SystemDept', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2024-02-29 01:06:28', b'0'); @@ -1707,7 +1862,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2431, '回款计划更新', 'crm:receivable-plan:update', 3, 3, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2432, '回款计划删除', 'crm:receivable-plan:delete', 3, 4, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2433, '回款计划导出', 'crm:receivable-plan:export', 3, 5, 2428, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 11:18:09', '', '2023-10-29 11:18:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2435, '商城装修', '', 2, 20, 2030, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', '', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '1', '2025-03-15 21:34:33', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2436, '装修模板', '', 2, 1, 2435, 'diy-template', 'fa6-solid:brush', 'mall/promotion/diy/template/index', 'DiyTemplate', 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2437, '装修模板查询', 'promotion:diy-template:query', 3, 1, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2438, '装修模板创建', 'promotion:diy-template:create', 3, 2, 2436, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 14:19:25', '', '2023-10-29 14:19:25', b'0'); @@ -2035,26 +2190,6 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2811, '积分商城活动更新', 'promotion:point-activity:update', 3, 3, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:10', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2812, '积分商城活动删除', 'promotion:point-activity:delete', 3, 4, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:12', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2813, '积分商城活动导出', 'promotion:point-activity:export', 3, 5, 2808, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-21 05:36:42', '1', '2024-09-22 14:49:27', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2892, 'IOT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:29', '1', '2024-08-10 09:55:29', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2893, '设备接入', '', 1, 1, 2892, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2024-10-20 18:57:43', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2894, '产品管理', '', 2, 0, 2893, 'product', '', 'iot/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2024-09-16 19:50:42', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2895, '产品查询', 'iot:product:query', 3, 1, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2896, '产品创建', 'iot:product:create', 3, 2, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2897, '产品更新', 'iot:product:update', 3, 3, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2898, '产品删除', 'iot:product:delete', 3, 4, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2899, '产品导出', 'iot:product:export', 3, 5, 2894, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-08-10 02:38:02', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2900, '设备管理', '', 2, 0, 2893, 'device', '', 'iot/device/index', 'IoTDevice', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:50:53', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2901, '设备查询', 'iot:device:query', 3, 1, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:00', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2902, '设备创建', 'iot:device:create', 3, 2, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2903, '设备更新', 'iot:device:update', 3, 3, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:18', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2904, '设备删除', 'iot:device:delete', 3, 4, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:42', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2905, '设备导出', 'iot:device:export', 3, 5, 2900, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-09-16 19:37:49', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2906, 'IoT 产品物模型管理', '', 1, 0, 2893, 'think-model-function', '', '', '', 0, b'0', b'1', b'1', '', '2024-09-25 22:12:09', '1', '2024-09-29 20:52:12', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2907, 'IoT 产品物模型查询', 'iot:think-model-function:query', 3, 1, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2908, 'IoT 产品物模型创建', 'iot:think-model-function:create', 3, 2, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2909, 'IoT 产品物模型更新', 'iot:think-model-function:update', 3, 3, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2910, 'IoT 产品物模型删除', 'iot:think-model-function:delete', 3, 4, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2911, 'IoT 产品物模型导出', 'iot:think-model-function:export', 3, 5, 2906, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-09-25 22:12:09', '', '2024-09-25 22:12:09', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2912, '创建推广员', 'trade:brokerage-user:create', 3, 7, 2346, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-01 14:32:39', '1', '2024-12-01 14:32:39', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2913, '流程清理', 'bpm:model:clean', 3, 7, 1193, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-17 19:32:06', '1', '2025-01-17 19:32:06', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2914, '积分商城活动关闭', 'promotion:point-activity:close', 3, 6, 2808, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-23 20:23:34', '1', '2025-01-23 20:23:34', b'0'); @@ -2068,6 +2203,57 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2922, '工具创建', 'ai:tool:create', 3, 2, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2923, '工具更新', 'ai:tool:update', 3, 3, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2924, '工具删除', 'ai:tool:delete', 3, 4, 2920, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2025-03-14 11:19:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4000, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:28', '1', '2024-12-07 15:58:34', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4001, '设备接入', '', 1, 2, 4000, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2025-02-27 08:39:49', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4002, '产品管理', '', 2, 2, 4001, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2024-12-07 18:47:53', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4003, '产品查询', 'iot:product:query', 3, 1, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:00', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4004, '产品创建', 'iot:product:create', 3, 2, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4005, '产品更新', 'iot:product:update', 3, 3, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:05', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4006, '产品删除', 'iot:product:delete', 3, 4, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:06', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4007, '产品导出', 'iot:product:export', 3, 5, 4002, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2024-12-07 15:55:13', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4008, '设备管理', '', 2, 4, 4001, 'device', 'fa:mobile', 'iot/device/device/index', 'IoTDevice', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-14 11:39:30', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4009, '设备查询', 'iot:device:query', 3, 1, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:40', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4010, '设备创建', 'iot:device:create', 3, 2, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:41', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4011, '设备更新', 'iot:device:update', 3, 3, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:42', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4012, '设备删除', 'iot:device:delete', 3, 4, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:43', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4013, '设备导出', 'iot:device:export', 3, 5, 4008, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-09-16 18:48:19', '1', '2024-12-07 15:55:44', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4014, '产品分类', '', 2, 1, 4001, 'product-category', 'ep:notebook', 'iot/product/category/index', 'IotProductCategory', 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '1', '2024-12-07 16:31:52', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4015, '产品分类查询', 'iot:product-category:query', 3, 1, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4016, '产品分类创建', 'iot:product-category:create', 3, 2, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4017, '产品分类更新', 'iot:product-category:update', 3, 3, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4018, '产品分类删除', 'iot:product-category:delete', 3, 4, 4014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-07 16:01:35', '', '2024-12-07 16:01:35', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4025, '插件管理', '', 2, 5, 4047, 'plugin-config', 'ep:folder-opened', 'iot/plugin/index', 'IoTPlugin', 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '1', '2025-02-05 22:23:12', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4026, '插件查询', 'iot:plugin-config:query', 3, 1, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:20', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4027, '插件创建', 'iot:plugin-config:create', 3, 2, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:16', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4028, '插件更新', 'iot:plugin-config:update', 3, 3, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:12', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4029, '插件删除', 'iot:plugin-config:delete', 3, 4, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4030, '插件导出', 'iot:plugin-config:export', 3, 5, 4025, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-09 21:25:06', '', '2025-02-05 21:23:06', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4031, '设备分组', '', 2, 3, 4001, 'device-group', 'fa-solid:layer-group', 'iot/device/group/index', 'IotDeviceGroup', 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '1', '2024-12-14 17:09:17', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4032, '设备分组查询', 'iot:device-group:query', 3, 1, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4033, '设备分组创建', 'iot:device-group:create', 3, 2, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4034, '设备分组更新', 'iot:device-group:update', 3, 3, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4035, '设备分组删除', 'iot:device-group:delete', 3, 4, 4031, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-14 17:08:29', '', '2024-12-14 17:08:29', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4036, '设备导入', 'iot:device:import', 3, 6, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-12-15 10:35:47', '1', '2024-12-15 10:35:47', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4037, '产品物模型', '', 2, 2, 4001, 'thing-model', 'ep:mostly-cloudy', 'iot/thingmodel/index', 'IoTThingModel', 0, b'0', b'0', b'0', '', '2024-12-16 17:17:50', '1', '2024-12-27 11:03:37', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4038, '产品物模型功能查询', 'iot:thing-model:query', 3, 1, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:51', '', '2025-03-17 09:14:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4039, '产品物模型功能创建', 'iot:thing-model:create', 3, 2, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:14:58', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4040, '产品物模型功能更新', 'iot:thing-model:update', 3, 3, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:03', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4041, '产品物模型功能删除', 'iot:thing-model:delete', 3, 4, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:52', '', '2025-03-17 09:15:06', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4042, '产品物模型功能导出', 'iot:thing-model:export', 3, 5, 4037, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-12-16 17:17:53', '', '2025-03-17 09:15:09', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4043, '设备上行', 'iot:device:upstream', 3, 7, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 04:40:16', '1', '2025-01-31 22:45:53', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4044, '设备属性查询', 'iot:device:property-query', 3, 10, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:52:54', '1', '2025-01-28 11:52:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4045, '设备日志查询', 'iot:device:log-query', 3, 11, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-28 11:53:22', '1', '2025-01-28 11:53:22', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4046, '设备下行', 'iot:device:downstream', 3, 8, 4008, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-01-31 22:46:11', '1', '2025-01-31 22:46:11', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4047, '运维管理', '', 1, 2, 4000, 'operations', 'fa:cog', '', '', 0, b'1', b'1', b'1', '1', '2025-02-05 22:21:37', '1', '2025-02-05 22:22:53', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4048, '规则引擎', '', 1, 3, 4000, 'rule', 'fa-solid:cogs', '', '', 0, b'1', b'1', b'1', '1', '2025-02-11 14:10:54', '1', '2025-02-11 14:10:54', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4049, '场景联动', '', 2, 1, 4048, 'scene', 'ep:link', 'iot/rule/scene/index', 'Scene', 0, b'1', b'1', b'1', '1', '2025-02-11 14:12:44', '1', '2025-02-12 10:15:36', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4050, 'IoT首页', '', 2, 1, 4000, 'home', 'ep:home-filled', 'iot/home/index', 'IotHome', 0, b'1', b'1', b'1', '1', '2025-02-27 08:39:35', '1', '2025-02-27 08:40:28', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4051, '数据桥梁', '', 2, 0, 4048, 'data-bridge', 'ep:guide', 'iot/rule/databridge/index', 'IotDataBridge', 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '1', '2025-03-09 13:47:51', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4052, 'IoT 数据桥梁查询', 'iot:data-bridge:query', 3, 1, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4053, 'IoT 数据桥梁创建', 'iot:data-bridge:create', 3, 2, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4054, 'IoT 数据桥梁更新', 'iot:data-bridge:update', 3, 3, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:11', '', '2025-03-09 13:47:11', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4055, 'IoT 数据桥梁删除', 'iot:data-bridge:delete', 3, 4, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', b'0'); +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4056, 'IoT 数据桥梁导出', 'iot:data-bridge:export', 3, 5, 4051, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-09 13:47:12', '', '2025-03-09 13:47:12', b'0'); COMMIT; -- ---------------------------- @@ -2189,7 +2375,7 @@ CREATE TABLE `system_oauth2_access_token` ( PRIMARY KEY (`id`) USING BTREE, INDEX `idx_access_token`(`access_token` ASC) USING BTREE, INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 13666 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 13787 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; -- ---------------------------- -- Records of system_oauth2_access_token @@ -2311,7 +2497,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1732 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; +) ENGINE = InnoDB AUTO_INCREMENT = 1735 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; -- ---------------------------- -- Records of system_oauth2_refresh_token @@ -3326,7 +3512,7 @@ CREATE TABLE `system_sms_code` ( `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', PRIMARY KEY (`id`) USING BTREE, INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号' -) ENGINE = InnoDB AUTO_INCREMENT = 646 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; +) ENGINE = InnoDB AUTO_INCREMENT = 649 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码'; -- ---------------------------- -- Records of system_sms_code @@ -3367,7 +3553,7 @@ CREATE TABLE `system_sms_log` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 1255 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; +) ENGINE = InnoDB AUTO_INCREMENT = 1279 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; -- ---------------------------- -- Records of system_sms_log @@ -3397,7 +3583,7 @@ CREATE TABLE `system_sms_template` ( `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', PRIMARY KEY (`id`) USING BTREE -) ENGINE = InnoDB AUTO_INCREMENT = 18 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板'; +) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信模板'; -- ---------------------------- -- Records of system_sms_template @@ -3416,6 +3602,7 @@ INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `cont INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, 1, 0, 'user-update-password', '会员用户 - 修改密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-08-19 11:34:18', b'0'); INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (16, 1, 0, 'user-reset-password', '会员用户 - 重置密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2023-08-19 18:58:01', '1', '2023-12-02 22:35:27', b'0'); INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (17, 2, 0, 'bpm_task_timeout', '【工作流】任务审批超时', '您收到了一条超时的待办任务:{processInstanceName}-{taskName},处理链接:{detailUrl}', '[\"processInstanceName\",\"taskName\",\"detailUrl\"]', '', 'X', 4, 'DEBUG_DING_TALK', '1', '2024-08-16 21:59:15', '1', '2024-08-16 21:59:34', b'0'); +INSERT INTO `system_sms_template` (`id`, `type`, `status`, `code`, `name`, `content`, `params`, `remark`, `api_template_id`, `channel_id`, `channel_code`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (18, 1, 0, 'admin-reset-password', '后台用户 - 忘记密码', '您的验证码{code},该验证码 5 分钟内有效,请勿泄漏于他人!', '[\"code\"]', '', 'null', 4, 'DEBUG_DING_TALK', '1', '2025-03-16 14:19:34', '1', '2025-03-16 14:19:45', b'0'); COMMIT; -- ---------------------------- @@ -3665,7 +3852,7 @@ CREATE TABLE `system_users` ( -- Records of system_users -- ---------------------------- BEGIN; -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2025-03-13 12:44:59', 'admin', '2021-01-05 17:03:47', NULL, '2025-03-13 12:44:59', b'0', 1); +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$04$Q3WCEQJbSZ0zT/7ryYTb3OgtrhwIZXu4ah5RQ5/YQDQ7DpW7N7oNa', '芋道源码', '管理员', 103, '[1,2]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/bf2002b38950c904243be7c825d3f82e29f25a44526583c3fde2ebdff3a87f75.png', 0, '0:0:0:0:0:0:0:1', '2025-03-16 14:20:16', 'admin', '2021-01-05 17:03:47', NULL, '2025-03-16 14:20:16', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$IgUse/ibRzAZ3rngCThmtemJeoh15Ux1TQ2hIMe4iwt/K3LcFHEda', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-11-02 14:00:46', '', '2021-01-07 09:07:17', NULL, '2024-11-02 14:00:46', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$fUBSmjKCPYAUmnMzOb6qE.eZCGPhHi1JmAKclODbfS/O7fHOl2bH6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-08-11 17:48:12', '', '2021-01-13 23:50:35', NULL, '2024-08-11 17:48:12', b'0', 1); INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2025-01-04 10:40:49', '', '2021-01-21 02:13:53', NULL, '2025-01-04 10:40:49', b'0', 1); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java index 093060e844..b04f426dc4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java @@ -12,6 +12,7 @@ import com.google.common.collect.Lists; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.MockedStatic; +import org.mockito.stubbing.Answer; import java.time.LocalDateTime; import java.util.List; @@ -50,6 +51,8 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { // mock 方法 httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) .thenReturn("{\"Message\":\"OK\",\"RequestId\":\"30067CE9-3710-5984-8881-909B21D8DB28\",\"Code\":\"OK\",\"BizId\":\"800025323183427988\"}"); + httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) + .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); // 调用 SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, @@ -75,6 +78,8 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { // mock 方法 httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) .thenReturn("{\"Message\":\"手机号码格式错误\",\"RequestId\":\"B7700B8E-227E-5886-9564-26036172F01F\",\"Code\":\"isv.MOBILE_NUMBER_ILLEGAL\"}"); + httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) + .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); // 调用 SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); @@ -127,6 +132,8 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { // mock 方法 httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) .thenReturn("{\"TemplateCode\":\"SMS_207945135\",\"RequestId\":\"6F4CC077-29C8-5BA5-AB62-5FF95068A5AC\",\"Message\":\"OK\",\"TemplateContent\":\"您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!\",\"TemplateName\":\"公告通知\",\"TemplateType\":0,\"Code\":\"OK\",\"CreateDate\":\"2020-12-23 17:34:42\",\"Reason\":\"无审批备注\",\"TemplateStatus\":1}"); + httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) + .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); // 调用 SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index daac5f2dcb..77438ef2af 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -68,21 +68,21 @@ spring: url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true username: root password: 123456 - tdengine: # IoT 数据库 - lazy: true # 开启懒加载,保证启动速度 - url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro - driver-class-name: com.taosdata.jdbc.rs.RestfulDriver - username: root - password: taosdata - druid: - validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL +# tdengine: # IoT 数据库(需要 IoT 物联网再开启噢!) +# url: jdbc:TAOS-RS://127.0.0.1:6041/ruoyi_vue_pro +# driver-class-name: com.taosdata.jdbc.rs.RestfulDriver +# username: root +# password: taosdata +# druid: +# validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 - redis: - host: 127.0.0.1 # 地址 - port: 6379 # 端口 - database: 0 # 数据库索引 -# password: dev # 密码,建议生产环境开启 + data: + redis: + host: 127.0.0.1 # 地址 + port: 6379 # 端口 + database: 0 # 数据库索引 + # password: dev # 密码,建议生产环境开启 --- #################### 定时任务相关配置 #################### From e9a99c1e27539f66ace7e069755890b0b107448f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 17 Mar 2025 13:17:29 +0800 Subject: [PATCH 304/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B4=E4=BD=93=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index b102d2d3d6..142869ff97 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ yudao-module-system yudao-module-infra - yudao-module-bpm + @@ -24,7 +24,7 @@ - yudao-module-iot + ${project.artifactId} From 6639d371328a51bdc70ea43e09d74f35f3d6af0e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 17 Mar 2025 18:50:12 +0800 Subject: [PATCH 305/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B4=E4=BD=93=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/job/rule/IotRuleSceneJob.java | 3 +- .../service/device/IotDeviceGroupService.java | 3 +- .../device/data/IotDeviceLogServiceImpl.java | 5 +- .../upstream/IotDeviceUpstreamServer.java | 53 ++++++++++--------- 4 files changed, 36 insertions(+), 28 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java index 2cda2fc20b..594f9ef0b0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/rule/IotRuleSceneJob.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.iot.job.rule; +import cn.hutool.core.map.MapUtil; import cn.iocoder.yudao.module.iot.enums.rule.IotRuleSceneTriggerTypeEnum; import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService; import jakarta.annotation.Resource; @@ -41,7 +42,7 @@ public class IotRuleSceneJob extends QuartzJobBean { * @return JobData Map */ public static Map buildJobDataMap(Long ruleSceneId) { - return Map.of(JOB_DATA_KEY_RULE_SCENE_ID, ruleSceneId); + return MapUtil.of(JOB_DATA_KEY_RULE_SCENE_ID, ruleSceneId); } /** diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java index 45e6ab25ef..5d074adb55 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO; @@ -48,7 +49,7 @@ public interface IotDeviceGroupService { */ default List validateDeviceGroupExists(Collection ids) { if (CollUtil.isEmpty(ids)) { - return List.of(); + return ListUtil.empty(); } return convertList(ids, this::validateDeviceGroupExists); } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java index 1df4d4cd44..2ed2312bbe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/data/IotDeviceLogServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.iot.service.device.data; import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -87,7 +88,7 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { Long timeMillis = timestamp.getTime(); // 消息数量转换 Integer count = ((Number) map.get("data")).intValue(); - return Map.of(timeMillis, count); + return MapUtil.of(timeMillis, count); }) .collect(Collectors.toList()); } @@ -103,7 +104,7 @@ public class IotDeviceLogServiceImpl implements IotDeviceLogService { Long timeMillis = timestamp.getTime(); // 消息数量转换 Integer count = ((Number) map.get("data")).intValue(); - return Map.of(timeMillis, count); + return MapUtil.of(timeMillis, count); }) .collect(Collectors.toList()); } diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java index 8911a76a80..00792ebcf9 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-emqx/src/main/java/cn/iocoder/yudao/module/iot/plugin/emqx/upstream/IotDeviceUpstreamServer.java @@ -85,11 +85,12 @@ public class IotDeviceUpstreamServer { } log.info("[start][开始启动服务]"); + // TODO @haohao:建议先启动 MQTT Broker,再启动 HTTP Server。类似 jdbc 先连接了,在启动 tomcat 的味道 // 1. 启动 HTTP 服务器 CompletableFuture httpFuture = server.listen(emqxProperties.getAuthPort()) .toCompletionStage() .toCompletableFuture() - .thenAccept(v -> log.info("[start][HTTP服务器启动完成,端口: {}]", server.actualPort())); + .thenAccept(v -> log.info("[start][HTTP 服务器启动完成,端口: {}]", server.actualPort())); // 2. 连接 MQTT Broker CompletableFuture mqttFuture = connectMqtt() @@ -98,16 +99,16 @@ public class IotDeviceUpstreamServer { .thenAccept(v -> { // 2.1 添加 MQTT 断开重连监听器 client.closeHandler(closeEvent -> { - log.warn("[closeHandler][MQTT连接已断开,准备重连]"); + log.warn("[closeHandler][MQTT 连接已断开,准备重连]"); reconnectWithDelay(); }); - // 2. 设置 MQTT 消息处理器 + // 2.2 设置 MQTT 消息处理器 setupMessageHandler(); }); // 3. 等待所有服务启动完成 CompletableFuture.allOf(httpFuture, mqttFuture) - .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) + .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) // TODO @芋艿:JDK8 不兼容 .whenComplete((result, error) -> { if (error != null) { log.error("[start][服务启动失败]", error); @@ -123,7 +124,7 @@ public class IotDeviceUpstreamServer { */ private void setupMessageHandler() { client.publishHandler(mqttMessageHandler::handle); - log.debug("[setupMessageHandler][MQTT消息处理器设置完成]"); + log.debug("[setupMessageHandler][MQTT 消息处理器设置完成]"); } /** @@ -203,26 +204,30 @@ public class IotDeviceUpstreamServer { isRunning = false; try { - CompletableFuture serverFuture = server != null - ? server.close().toCompletionStage().toCompletableFuture() - : CompletableFuture.completedFuture(null); - CompletableFuture clientFuture = client != null - ? client.disconnect().toCompletionStage().toCompletableFuture() - : CompletableFuture.completedFuture(null); - CompletableFuture vertxFuture = vertx != null - ? vertx.close().toCompletionStage().toCompletableFuture() - : CompletableFuture.completedFuture(null); + // 关闭 HTTP 服务器 + if (server != null) { + server.close() + .toCompletionStage() + .toCompletableFuture() + .join(); + } - // 等待所有资源关闭 - CompletableFuture.allOf(serverFuture, clientFuture, vertxFuture) - .orTimeout(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS) - .whenComplete((result, error) -> { - if (error != null) { - log.error("[stop][服务关闭过程中发生异常]", error); - } else { - log.info("[stop][所有服务关闭完成]"); - } - }); + // 关闭 MQTT 客户端 + if (client != null) { + client.disconnect() + .toCompletionStage() + .toCompletableFuture() + .join(); + } + + // 关闭 Vertx 实例 + if (vertx!= null) { + vertx.close() + .toCompletionStage() + .toCompletableFuture() + .join(); + } + log.info("[stop][关闭完成]"); } catch (Exception e) { log.error("[stop][关闭服务异常]", e); throw new RuntimeException("关闭 IoT 设备上行服务失败", e); From 71add4b0586368181a569bdf0fb3d7272bc1986d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 17 Mar 2025 20:45:26 +0800 Subject: [PATCH 306/309] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91IoT=EF=BC=9A=E6=95=B4=E4=BD=93=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/mybatis/core/type/EncryptTypeHandler.java | 2 +- .../iot/framework/job/core/IotSchedulerManager.java | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java index 7ef0f4ece4..9327ebbfed 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/type/EncryptTypeHandler.java @@ -13,7 +13,7 @@ import java.sql.ResultSet; import java.sql.SQLException; /** - * 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现 + * 字段字段的 TypeHandler 实现类,基于 {@link AES} 实现 * 可通过 jasypt.encryptor.password 配置项,设置密钥 * * @author 芋道源码 diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java index c52164b6e9..015b9ec3f0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/job/core/IotSchedulerManager.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.iot.framework.job.core; -import cn.iocoder.yudao.framework.quartz.core.enums.JobDataKeyEnum; import lombok.extern.slf4j.Slf4j; import org.quartz.*; import org.springframework.context.ApplicationContext; @@ -173,12 +172,8 @@ public class IotSchedulerManager { * @param jobName 任务名 * @throws SchedulerException 触发异常 */ - public void triggerJob(String jobName) - throws SchedulerException { - // 触发任务 - JobDataMap data = new JobDataMap(); - data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobName); - scheduler.triggerJob(new JobKey(jobName), data); + public void triggerJob(String jobName) throws SchedulerException { + scheduler.triggerJob(new JobKey(jobName)); } private Trigger buildTrigger(String jobName, String cronExpression) { From 9b11199665f00790ecabad6e10f449c4148635f1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Mar 2025 09:41:22 +0800 Subject: [PATCH 307/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91system=EF=BC=9A=E5=A2=9E=E5=8A=A0=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E7=9A=84=E4=B8=8B=E6=8B=89=E9=80=89=E6=8B=A9=EF=BC=8C?= =?UTF-8?q?=E7=94=A8=E4=BA=8E=E7=99=BB=E5=BD=95=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/auth/AuthController.java | 16 ++------------ .../admin/permission/MenuController.java | 4 ++-- .../admin/tenant/TenantController.java | 22 ++++++++++++++----- .../tenant/vo/tenant/TenantSimpleRespVO.java | 16 -------------- .../system/dal/mysql/tenant/TenantMapper.java | 4 ++++ .../system/service/tenant/TenantService.java | 10 ++++++++- .../service/tenant/TenantServiceImpl.java | 5 +++++ .../src/main/resources/application.yaml | 1 + 8 files changed, 40 insertions(+), 38 deletions(-) delete mode 100755 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSimpleRespVO.java diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index 1e7a99a322..d9269470d0 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -7,14 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthLoginRespVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthPermissionInfoRespVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthResetPasswordReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsLoginReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSmsSendReqVO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthSocialLoginReqVO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; @@ -36,12 +29,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.Collections; import java.util.List; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java index b6d067f1fb..824fe41048 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java @@ -67,8 +67,8 @@ public class MenuController { } @GetMapping({"/list-all-simple", "simple-list"}) - @Operation(summary = "获取菜单精简信息列表", description = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。" + - "在多租户的场景下,会只返回租户所在套餐有的菜单") + @Operation(summary = "获取菜单精简信息列表", + description = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。在多租户的场景下,会只返回租户所在套餐有的菜单") public CommonResult> getSimpleMenuList() { List list = menuService.getMenuListByTenant( new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java index 090c6a80ff..f698b92d0c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.controller.admin.tenant; import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSimpleRespVO; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; import cn.iocoder.yudao.module.system.service.tenant.TenantService; import io.swagger.v3.oas.annotations.Operation; @@ -27,6 +27,7 @@ import java.util.List; import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @Tag(name = "管理后台 - 租户") @RestController @@ -45,13 +46,25 @@ public class TenantController { return success(tenant != null ? tenant.getId() : null); } + @GetMapping({ "simple-list" }) + @PermitAll + @Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项") + public CommonResult> getTenantSimpleList() { + List list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, tenantDO -> + new TenantRespVO().setId(tenantDO.getId()).setName(tenantDO.getName()))); + } + @GetMapping("/get-by-website") @PermitAll @Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息") @Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn") - public CommonResult getTenantByWebsite(@RequestParam("website") String website) { + public CommonResult getTenantByWebsite(@RequestParam("website") String website) { TenantDO tenant = tenantService.getTenantByWebsite(website); - return success(BeanUtils.toBean(tenant, TenantSimpleRespVO.class)); + if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) { + return success(null); + } + return success(new TenantRespVO().setId(tenant.getId()).setName(tenant.getName())); } @PostMapping("/create") @@ -99,8 +112,7 @@ public class TenantController { @Operation(summary = "导出租户 Excel") @PreAuthorize("@ss.hasPermission('system:tenant:export')") @ApiAccessLog(operateType = EXPORT) - public void exportTenantExcel(@Valid TenantPageReqVO exportReqVO, - HttpServletResponse response) throws IOException { + public void exportTenantExcel(@Valid TenantPageReqVO exportReqVO, HttpServletResponse response) throws IOException { exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); List list = tenantService.getTenantPage(exportReqVO).getList(); // 导出 Excel diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSimpleRespVO.java deleted file mode 100755 index 49752278da..0000000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSimpleRespVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 租户精简 Response VO") -@Data -public class TenantSimpleRespVO { - - @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java index aaca0160ab..8ddf06065a 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java @@ -43,4 +43,8 @@ public interface TenantMapper extends BaseMapperX { return selectList(TenantDO::getPackageId, packageId); } + default List selectListByStatus(Integer status) { + return selectList(TenantDO::getStatus, status); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java index e0bd9d291c..425d18d609 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java @@ -38,7 +38,7 @@ public interface TenantService { * 更新租户的角色菜单 * * @param tenantId 租户编号 - * @param menuIds 菜单编号数组 + * @param menuIds 菜单编号数组 */ void updateTenantRoleMenu(Long tenantId, Set menuIds); @@ -97,6 +97,14 @@ public interface TenantService { */ List getTenantListByPackageId(Long packageId); + /** + * 获得指定状态的租户列表 + * + * @param status 状态 + * @return 租户列表 + */ + List getTenantListByStatus(Integer status); + /** * 进行租户的信息处理逻辑 * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java index b5b3c358ef..c3b09ec5a9 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java @@ -265,6 +265,11 @@ public class TenantServiceImpl implements TenantService { return tenantMapper.selectListByPackageId(packageId); } + @Override + public List getTenantListByStatus(Integer status) { + return tenantMapper.selectListByStatus(status); + } + @Override public void handleTenantInfo(TenantInfoHandler handler) { // 如果禁用,则不执行逻辑 diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 3941181b2d..13683df543 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -271,6 +271,7 @@ yudao: ignore-urls: - /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号 - /admin-api/system/tenant/get-by-website # 基于域名获取租户,不许带租户编号 + - /admin-api/system/tenant/simple-list # 获取租户列表,不许带租户编号 - /admin-api/system/captcha/get # 获取图片验证码,和租户无关 - /admin-api/system/captcha/check # 校验图片验证码,和租户无关 - /admin-api/infra/file/*/get/** # 获取图片,和租户无关 From 0c9dd349812b10090a965c00d4f2fe64466cdcbb Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 09:00:33 +0800 Subject: [PATCH 308/309] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91IOT=EF=BC=9A=E5=A4=9A=E4=B8=AA=20ProductCateg?= =?UTF-8?q?oryMapper=20=E7=9A=84=E5=90=8D=E5=AD=97=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IotProductCategoryServiceImpl.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java index f03186866d..13a8d488e9 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductCategoryServiceImpl.java @@ -29,7 +29,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_CATEG public class IotProductCategoryServiceImpl implements IotProductCategoryService { @Resource - private IotProductCategoryMapper productCategoryMapper; + private IotProductCategoryMapper iotProductCategoryMapper; @Resource private IotProductService productService; @@ -40,7 +40,7 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService public Long createProductCategory(IotProductCategorySaveReqVO createReqVO) { // 插入 IotProductCategoryDO productCategory = BeanUtils.toBean(createReqVO, IotProductCategoryDO.class); - productCategoryMapper.insert(productCategory); + iotProductCategoryMapper.insert(productCategory); // 返回 return productCategory.getId(); } @@ -51,7 +51,7 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService validateProductCategoryExists(updateReqVO.getId()); // 更新 IotProductCategoryDO updateObj = BeanUtils.toBean(updateReqVO, IotProductCategoryDO.class); - productCategoryMapper.updateById(updateObj); + iotProductCategoryMapper.updateById(updateObj); } @Override @@ -59,18 +59,18 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService // 校验存在 validateProductCategoryExists(id); // 删除 - productCategoryMapper.deleteById(id); + iotProductCategoryMapper.deleteById(id); } private void validateProductCategoryExists(Long id) { - if (productCategoryMapper.selectById(id) == null) { + if (iotProductCategoryMapper.selectById(id) == null) { throw exception(PRODUCT_CATEGORY_NOT_EXISTS); } } @Override public IotProductCategoryDO getProductCategory(Long id) { - return productCategoryMapper.selectById(id); + return iotProductCategoryMapper.selectById(id); } @Override @@ -78,28 +78,28 @@ public class IotProductCategoryServiceImpl implements IotProductCategoryService if (CollUtil.isEmpty(ids)) { return CollUtil.newArrayList(); } - return productCategoryMapper.selectBatchIds(ids); + return iotProductCategoryMapper.selectBatchIds(ids); } @Override public PageResult getProductCategoryPage(IotProductCategoryPageReqVO pageReqVO) { - return productCategoryMapper.selectPage(pageReqVO); + return iotProductCategoryMapper.selectPage(pageReqVO); } @Override public List getProductCategoryListByStatus(Integer status) { - return productCategoryMapper.selectListByStatus(status); + return iotProductCategoryMapper.selectListByStatus(status); } @Override public Long getProductCategoryCount(LocalDateTime createTime) { - return productCategoryMapper.selectCountByCreateTime(createTime); + return iotProductCategoryMapper.selectCountByCreateTime(createTime); } @Override public Map getProductCategoryDeviceCountMap() { // 1. 获取所有数据 - List categoryList = productCategoryMapper.selectList(); + List categoryList = iotProductCategoryMapper.selectList(); List productList = productService.getProductList(); // TODO @super:不要 list 查询,返回内存,而是查询一个 Map Map deviceCountMapByProductId = deviceService.getDeviceCountMapByProductId(); From b634a1e97b978734b885de7b01c9fc8ec968f5e0 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 16:54:48 +0800 Subject: [PATCH 309/309] =?UTF-8?q?=E3=80=90=E7=BC=BA=E9=99=B7=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91CRM=EF=BC=9A=E4=BF=AE=E5=A4=8D=E8=B7=9F?= =?UTF-8?q?=E8=BF=9B=E6=97=A5=E5=BF=97=E7=9A=84=E6=93=8D=E4=BD=9C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/crm/service/contact/CrmContactServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java index 174db9b3a2..9348c5e463 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java @@ -223,7 +223,7 @@ public class CrmContactServiceImpl implements CrmContactService { } @Override - @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}", + @LogRecord(type = CRM_CONTACT_TYPE, subType = CRM_CONTACT_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}}", success = CRM_CONTACT_FOLLOW_UP_SUCCESS) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#id", level = CrmPermissionLevelEnum.WRITE) public void updateContactFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) {