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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] 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/386] 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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] =?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/386] 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/386] =?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/386] =?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/386] =?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/386] =?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 5264de077d71f2a24414caf75497d6519cdf9e8f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 25 Jan 2025 11:26:10 +0800 Subject: [PATCH 098/386] =?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 099/386] =?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 100/386] =?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 101/386] =?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 102/386] =?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 103/386] =?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 104/386] =?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 105/386] =?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 106/386] =?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 107/386] =?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 108/386] =?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 109/386] =?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 110/386] =?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 113/386] =?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 114/386] =?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 115/386] =?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 131/386] =?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 132/386] =?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 133/386] =?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 134/386] =?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 135/386] =?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 136/386] =?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 137/386] =?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 138/386] =?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 139/386] =?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 140/386] =?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 141/386] =?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 142/386] =?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 143/386] =?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 144/386] =?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 145/386] =?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 146/386] =?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 147/386] =?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 148/386] =?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 149/386] =?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 150/386] =?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 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 151/386] =?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 152/386] =?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 153/386] =?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 154/386] =?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 155/386] =?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 156/386] =?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 157/386] =?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 158/386] =?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 159/386] =?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 160/386] =?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 161/386] =?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 162/386] =?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 d116e5eec1361c74bef04dd001884adb43fb1e26 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Feb 2025 07:26:01 +0800 Subject: [PATCH 163/386] =?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 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 164/386] =?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 0085b42518898b1d06aeb87a050976928fe97a6b Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 12 Feb 2025 13:53:15 +0800 Subject: [PATCH 165/386] =?UTF-8?q?feat:=20=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java | 1 + .../framework/flowable/core/util/SimpleModelUtils.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index 238a9347c3..c9da0718df 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -25,6 +25,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定 APPROVE_NODE(11, "审批人", "userTask"), COPY_NODE(12, "抄送人", "serviceTask"), + TRANSACTOR_NODE(13, "办理人", "userTask"), DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 309c11cf83..d6673fc54a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -40,7 +40,7 @@ public class SimpleModelUtils { static { List converts = asList(new StartNodeConvert(), new EndNodeConvert(), - new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), + new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(), new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); @@ -514,6 +514,13 @@ public class SimpleModelUtils { } + private static class TransactorNodeConvert extends ApproveNodeConvert { + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; + } + } + private static class CopyNodeConvert implements NodeConvert { @Override From e5aede6265bdbc77a6d9491c4e7660491c3ffcbe Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 12 Feb 2025 14:10:20 +0800 Subject: [PATCH 166/386] =?UTF-8?q?feat:=20=E5=8A=9E=E7=90=86=E4=BA=BA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E9=AB=98=E4=BA=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 3 ++- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d6673fc54a..2f18c475bf 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -792,10 +792,11 @@ public class SimpleModelUtils { BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); Assert.notNull(nodeType, "模型节点类型不支持"); - // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE + // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { // 添加元素 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ca50baef80..96aeaf24b8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -479,7 +479,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 1. 开始节点/审批节点 if (ObjectUtils.equalsAny(node.getType(), BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) { + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(), + BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); activityNode.setCandidateUserIds(candidateUserIds); From c27b02beb634aa8c702353efface531d2562d76c Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 12 Feb 2025 18:19:53 +0800 Subject: [PATCH 167/386] =?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 f2894328909b62af4eb88f62f9bd227aea6078d2 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 12 Feb 2025 18:46:40 +0800 Subject: [PATCH 168/386] =?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=E5=8A=9E=E7=90=86=E4=BA=BA?= =?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 --- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 2f18c475bf..e65c88a0f3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -515,10 +515,12 @@ public class SimpleModelUtils { } private static class TransactorNodeConvert extends ApproveNodeConvert { + @Override public BpmSimpleModelNodeTypeEnum getType() { return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; } + } private static class CopyNodeConvert implements NodeConvert { From 7cf55c5300bdf026ad042525b14eded6fa04a3bc Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Thu, 13 Feb 2025 09:33:12 +0800 Subject: [PATCH 169/386] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=B1=BB=E5=9E=8B=E4=BB=A5=E5=8C=BA=E5=88=86=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/enums/BpmnModelConstants.java | 5 +++++ .../flowable/core/util/BpmnModelUtils.java | 20 +++++++++++++++++++ .../flowable/core/util/SimpleModelUtils.java | 2 ++ .../task/BpmProcessInstanceServiceImpl.java | 5 +++-- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java index 9dce8a2eae..7363eedba6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -129,4 +129,9 @@ public interface BpmnModelConstants { */ String REASON_REQUIRE = "reasonRequire"; + /** + * 节点类型 + */ + String NODE_TYPE = "nodeType"; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 3f22c7c259..d85db6230e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -410,6 +410,26 @@ public class BpmnModelUtils { return parseExtensionElement(flowElement, TRIGGER_PARAM); } + /** + * 给节点添加节点类型 + * + * @param nodeType 节点类型 + * @param flowElement 节点 + */ + public static void addNodeType(Integer nodeType, FlowElement flowElement) { + addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType); + } + + /** + * 解析节点类型 + * + * @param flowElement 节点 + * @return 节点类型 + */ + public static Integer parseNodeType(FlowElement flowElement) { + return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE)); + } + // ========== BPM 简单查找相关的方法 ========== /** diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 2f18c475bf..319410f1ef 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -445,6 +445,8 @@ public class SimpleModelUtils { addSignEnable(node.getSignEnable(), userTask); // 审批意见 addReasonRequire(node.getReasonRequire(), userTask); + // 节点类型 + addNodeType(node.getType(), userTask); return userTask; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 96aeaf24b8..7b9b9e1687 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -67,6 +67,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.flowable.bpmn.constants.BpmnXMLConstants.*; @@ -325,7 +326,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + : parseNodeType(flowNode)) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -402,7 +403,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setNodeType(parseNodeType(flowNode)) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) From 94499c0e5f13b1a497f8f6ec73a7fd3c812f9d76 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Thu, 13 Feb 2025 19:49:49 +0800 Subject: [PATCH 170/386] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9B=BE=E6=A0=87=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 7bdc963c8a..dfbc74978b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -27,9 +27,7 @@ import java.util.List; @Data public class BpmModelMetaInfoVO { - @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") - @NotEmpty(message = "流程图标不能为空") - @URL(message = "流程图标格式不正确") + @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") private String icon; @Schema(description = "流程描述", example = "我是描述") From 8826f92d9dea4c35df3acc8ee5dd92fdc8ff1bda Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Feb 2025 19:59:01 +0800 Subject: [PATCH 171/386] =?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=E7=A7=BB=E9=99=A4=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=9B=BE=E6=A0=87=E9=9D=9E=E7=A9=BA=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=8C=E8=BF=98=E6=98=AF=E9=9C=80=E8=A6=81=20URL=20=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.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/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index dfbc74978b..e00b6d4d3b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; -import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.validation.InEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; @@ -28,6 +27,7 @@ import java.util.List; public class BpmModelMetaInfoVO { @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") + @URL(message = "流程图标格式不正确") private String icon; @Schema(description = "流程描述", example = "我是描述") From ef296822304ec8785e46243e7d3992e6498a6323 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 14 Feb 2025 08:22:04 +0800 Subject: [PATCH 172/386] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=B1=BB=E5=9E=8B=E4=BB=A5=E5=8C=BA=E5=88=86=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/controller/admin/task/vo/task/BpmTaskRespVO.java | 3 +++ .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 6 ++++-- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index 83812ee1ab..d36b61a2c7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -85,6 +85,9 @@ public class BpmTaskRespVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; + @Schema(description = "节点类型", example = "10") + private Integer nodeType; + @Data @Schema(description = "流程实例") public static class ProcessInstance { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 7b9b9e1687..d12fe23d04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -326,7 +326,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : parseNodeType(flowNode)) + : ObjectUtil.isNull(parseNodeType(flowNode)) ? + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -403,7 +404,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(parseNodeType(flowNode)) + .setNodeType(ObjectUtil.isNull(parseNodeType(flowNode)) ? + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) 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 76c7771035..73ae53b25e 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 @@ -162,6 +162,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { bpmnModel, todoTask.getTaskDefinitionKey()); Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); + Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey())); // 4. 任务表单 BpmFormDO taskForm = null; @@ -171,7 +172,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) .setSignEnable(signEnable) - .setReasonRequire(reasonRequire); + .setReasonRequire(reasonRequire) + .setNodeType(nodeType); } @Override From 00eea85a69e7223aa190d0737b355fbd0571ad9c Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 14 Feb 2025 08:31:15 +0800 Subject: [PATCH 173/386] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.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/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index d36b61a2c7..5ba3c3c85a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -85,7 +85,7 @@ public class BpmTaskRespVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; - @Schema(description = "节点类型", example = "10") + @Schema(description = "节点类型", example = "10") // 只有Simple设计器的场景下才会使用此字段 private Integer nodeType; @Data 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/386] =?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 045a224c2255a4add4c991b8ba2bf95533399bec Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Fri, 14 Feb 2025 15:51:57 +0800 Subject: [PATCH 175/386] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=81=9C?= =?UTF-8?q?=E7=94=A8=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/definition/BpmModelServiceImpl.java | 4 ++-- .../definition/BpmProcessDefinitionService.java | 3 ++- .../definition/BpmProcessDefinitionServiceImpl.java | 10 +++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 9ccc2f2c92..db3beeff5b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -304,7 +304,7 @@ public class BpmModelServiceImpl implements BpmModelService { } // 2. 更新状态 - processDefinitionService.updateProcessDefinitionState(definition.getId(), state); + processDefinitionService.updateProcessDefinitionState(definition.getId(), state,definition.isSuspended()); } @Override @@ -403,7 +403,7 @@ public class BpmModelServiceImpl implements BpmModelService { return; } processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), - SuspensionState.SUSPENDED.getStateCode()); + SuspensionState.SUSPENDED.getStateCode(),oldDefinition.isSuspended()); } private Model getModelByKey(String key) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java index f42e69e73b..9f388621f3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java @@ -60,8 +60,9 @@ public interface BpmProcessDefinitionService { * * @param id 流程定义的编号 * @param state 状态 + * @param isSuspended 是否处于激活或挂起状态 */ - void updateProcessDefinitionState(String id, Integer state); + void updateProcessDefinitionState(String id, Integer state, boolean isSuspended); /** * 更新模型编号 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index c6a178c6c8..cc9640f2f4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -155,17 +155,21 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ } @Override - public void updateProcessDefinitionState(String id, Integer state) { + public void updateProcessDefinitionState(String id, Integer state,boolean isSuspended) { // 激活 if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { - repositoryService.activateProcessDefinitionById(id, false, null); + if (isSuspended) { + repositoryService.activateProcessDefinitionById(id, false, null); + } return; } // 挂起 if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { // suspendProcessInstances = false,进行中的任务,不进行挂起。 // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 - repositoryService.suspendProcessDefinitionById(id, false, null); + if (!isSuspended) { + repositoryService.suspendProcessDefinitionById(id, false, null); + } return; } log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); From 75bca650da7954bac584d46a4cac428dbfbf49ca Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 08:29:50 +0800 Subject: [PATCH 176/386] =?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 e9a3773b7275ff60e03860f1dadd934629555620 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 08:48:51 +0800 Subject: [PATCH 177/386] =?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=E5=8A=9E=E7=90=86=E4=BA=BA?= =?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 --- .../bpm/controller/admin/task/vo/task/BpmTaskRespVO.java | 4 ++-- .../flowable/core/enums/BpmnModelConstants.java | 4 ++++ .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 9 +++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 4 +--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index 5ba3c3c85a..fce72a490a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -85,8 +85,8 @@ public class BpmTaskRespVO { @Schema(description = "是否填写审批意见", example = "false") private Boolean reasonRequire; - @Schema(description = "节点类型", example = "10") // 只有Simple设计器的场景下才会使用此字段 - private Integer nodeType; + @Schema(description = "节点类型", example = "10") + private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。 @Data @Schema(description = "流程实例") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java index 7363eedba6..cb857b7390 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -1,5 +1,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; + /** * BPMN XML 常量信息 * @@ -131,6 +133,8 @@ public interface BpmnModelConstants { /** * 节点类型 + * + * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点 */ String NODE_TYPE = "nodeType"; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index d12fe23d04..dc6788a9e2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -326,8 +327,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : ObjectUtil.isNull(parseNodeType(flowNode)) ? - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -404,8 +405,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(ObjectUtil.isNull(parseNodeType(flowNode)) ? - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType() : parseNodeType(flowNode)) + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) 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 73ae53b25e..b092e52f61 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 @@ -171,9 +171,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) - .setSignEnable(signEnable) - .setReasonRequire(reasonRequire) - .setNodeType(nodeType); + .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); } @Override From 56ea9d2381e6aa837ce54bc841cc018a14b562cc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 09:21:44 +0800 Subject: [PATCH 178/386] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91BPM=EF=BC=9A=E4=BF=AE=E5=A4=8D=E5=81=9C?= =?UTF-8?q?=E7=94=A8=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8A=A5=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/definition/BpmModelServiceImpl.java | 4 ++-- .../definition/BpmProcessDefinitionService.java | 3 +-- .../BpmProcessDefinitionServiceImpl.java | 14 +++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index db3beeff5b..9ccc2f2c92 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -304,7 +304,7 @@ public class BpmModelServiceImpl implements BpmModelService { } // 2. 更新状态 - processDefinitionService.updateProcessDefinitionState(definition.getId(), state,definition.isSuspended()); + processDefinitionService.updateProcessDefinitionState(definition.getId(), state); } @Override @@ -403,7 +403,7 @@ public class BpmModelServiceImpl implements BpmModelService { return; } processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), - SuspensionState.SUSPENDED.getStateCode(),oldDefinition.isSuspended()); + SuspensionState.SUSPENDED.getStateCode()); } private Model getModelByKey(String key) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java index 9f388621f3..f42e69e73b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionService.java @@ -60,9 +60,8 @@ public interface BpmProcessDefinitionService { * * @param id 流程定义的编号 * @param state 状态 - * @param isSuspended 是否处于激活或挂起状态 */ - void updateProcessDefinitionState(String id, Integer state, boolean isSuspended); + void updateProcessDefinitionState(String id, Integer state); /** * 更新模型编号 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index cc9640f2f4..96280fa088 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -28,8 +28,7 @@ import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static java.util.Collections.emptyList; /** @@ -155,10 +154,15 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ } @Override - public void updateProcessDefinitionState(String id, Integer state,boolean isSuspended) { + public void updateProcessDefinitionState(String id, Integer state) { + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); + if (processDefinition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + // 激活 if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { - if (isSuspended) { + if (processDefinition.isSuspended()) { repositoryService.activateProcessDefinitionById(id, false, null); } return; @@ -167,7 +171,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { // suspendProcessInstances = false,进行中的任务,不进行挂起。 // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 - if (!isSuspended) { + if (!processDefinition.isSuspended()) { repositoryService.suspendProcessDefinitionById(id, false, null); } return; From a4e090383b0d4339bbd3ca6f61a4317d4c2ff307 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Feb 2025 09:40:02 +0800 Subject: [PATCH 179/386] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91BPM=EF=BC=9A=E5=B7=B2=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=9A=84=E5=88=97=E8=A1=A8=EF=BC=8C=E5=BC=BA=E5=88=B6=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E8=87=AA=E5=8A=A8=E5=AE=8C=E6=88=90=E7=9A=84=E2=80=9C?= =?UTF-8?q?=E5=8F=91=E8=B5=B7=E4=BA=BA=E2=80=9D=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 5 +++++ 1 file changed, 5 insertions(+) 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 b092e52f61..f57f088ef5 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 @@ -61,6 +61,7 @@ import java.util.stream.Stream; 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.bpm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; @@ -194,6 +195,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { return PageResult.empty(); } List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + + // 特殊:强制移除自动完成的“发起人”节点 + // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 + tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); return new PageResult<>(tasks, count); } From 9a4bb60a783d5c4f4f4bcaa2e09961ed525aec83 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Sun, 16 Feb 2025 08:47:13 +0800 Subject: [PATCH 180/386] =?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=BF=AE=E6=94=B9=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=99=A8=E5=A2=9E=E5=8A=A0=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/definition/BpmTriggerTypeEnum.java | 3 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 18 ++++-- .../flowable/core/util/SimpleModelUtils.java | 4 +- .../task/trigger/BpmFormUpdateTrigger.java | 63 +++++++++++++++++++ .../trigger/BpmUpdateNormalFormTrigger.java | 44 ------------- 5 files changed, 78 insertions(+), 54 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java delete mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 9a2f073726..42d8bafabb 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -17,7 +17,7 @@ import java.util.Arrays; public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), - UPDATE_NORMAL_FORM(2, "更新流程表单"); // TODO @jason:FORM_UPDATE + FORM_UPDATE(2, "更新流程表单"); /** * 触发器执行动作类型 @@ -39,5 +39,4 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { public static BpmTriggerTypeEnum typeOf(Integer type) { return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 291cf4d830..50c45a96a2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -345,12 +345,10 @@ public class BpmSimpleModelNodeVO { @Valid private HttpRequestTriggerSetting httpRequestSetting; - // TODO @jason:这个要不直接叫 formSetting,更好理解一点哈 - // TODO @jason:如果搞成 List,是不是可以做条件组了?微信讨论哈 /** * 流程表单触发器设置 */ - private NormalFormTriggerSetting normalFormSetting; + private List formSettings; @Schema(description = "http 请求触发器设置", example = "{}") @Data @@ -383,12 +381,20 @@ public class BpmSimpleModelNodeVO { @Schema(description = "流程表单触发器设置", example = "{}") @Data - public static class NormalFormTriggerSetting { + public static class FormTriggerSetting { + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "条件组", example = "{}") + private ConditionGroups conditionGroups; @Schema(description = "修改的表单字段", example = "userName") private Map updateFormFields; - } - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 309c11cf83..55591468e3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -724,8 +724,8 @@ public class SimpleModelUtils { if (node.getTriggerSetting().getHttpRequestSetting() != null) { addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); } - if (node.getTriggerSetting().getNormalFormSetting() != null) { - addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getNormalFormSetting()); + if (node.getTriggerSetting().getFormSettings() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); } } return serviceTask; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java new file mode 100644 index 0000000000..6a007c0dfe --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * BPM 更新流程表单触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormUpdateTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_UPDATE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析更新流程表单配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); + return; + } + + // 2.获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3.更新流程变量 + for (FormTriggerSetting setting : settings) { + if (CollUtil.isEmpty(setting.getUpdateFormFields())) { + continue; + } + boolean isFormUpdateNeeded = true; + if (setting.getConditionType() != null) { // 配置了条件,判断条件是否满足 + String conditionExpression = SimpleModelUtils.buildConditionExpression(setting.getConditionType(), setting.getConditionExpression(), + setting.getConditionGroups()); + isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + if (isFormUpdateNeeded) { + processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); + } + } + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java deleted file mode 100644 index deab1f5e36..0000000000 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmUpdateNormalFormTrigger.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.bpm.service.task.trigger; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.NormalFormTriggerSetting; -import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; -import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -// TODO @jason:改成 BpmFormUpdateTrigger -/** - * BPM 更新流程表单触发器 - * - * @author jason - */ -@Component -@Slf4j -public class BpmUpdateNormalFormTrigger implements BpmTrigger { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.UPDATE_NORMAL_FORM; - } - - @Override - public void execute(String processInstanceId, String param) { - // 1. 解析更新流程表单配置 - NormalFormTriggerSetting setting = JsonUtils.parseObject(param, NormalFormTriggerSetting.class); - if (setting == null) { - log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); - return; - } - // 2.更新流程变量 - if (CollUtil.isNotEmpty(setting.getUpdateFormFields())) { - processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); - } - } - -} From 3d20ce1a9bdc56bdec2b55a98bb1c15ff76735d7 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 09:52:09 +0800 Subject: [PATCH 181/386] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E6=94=AF=E8=8A=82=E7=82=B9=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/BpmnModelUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 3f22c7c259..3705d697c9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -780,7 +780,8 @@ public class BpmnModelUtils { Gateway gateway = (Gateway) currentElement; SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); + //流程第一次发起时,variables条件值一定为空,发生异常会导致后续分支节点无法预测, + || (null != variables && evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); From 1023afda4051db10d7a3198a067a0b55461b7b1b Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 10:28:11 +0800 Subject: [PATCH 182/386] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=88=86=E6=94=AF=E8=8A=82=E7=82=B9=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 3705d697c9..12fa1bf6d9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -32,7 +32,7 @@ import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_P /** * BPMN Model 操作工具类。目前分成三部分: - * + *

* 1. BPMN 修改 + 解析元素相关的方法 * 2. BPMN 简单查找相关的方法 * 3. BPMN 复杂遍历相关的方法 @@ -105,8 +105,8 @@ public class BpmnModelUtils { * 给节点添加候选人元素 * * @param candidateStrategy 候选人策略 - * @param candidateParam 候选人参数,允许空 - * @param flowElement 节点 + * @param candidateParam 候选人参数,允许空 + * @param flowElement 节点 */ public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, @@ -150,9 +150,9 @@ public class BpmnModelUtils { /** * 解析审批类型 * - * @see BpmUserTaskApproveTypeEnum * @param userTask 任务节点 * @return 审批类型 + * @see BpmUserTaskApproveTypeEnum */ public static Integer parseApproveType(FlowElement userTask) { return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); @@ -162,7 +162,7 @@ public class BpmnModelUtils { * 添加任务拒绝处理元素 * * @param rejectHandler 任务拒绝处理 - * @param userTask 任务节点 + * @param userTask 任务节点 */ public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { if (rejectHandler == null) { @@ -196,9 +196,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 * - * @see BpmUserTaskAssignStartUserHandlerTypeEnum * @param assignStartUserHandlerType 发起人处理类型 - * @param userTask 任务节点 + * @param userTask 任务节点 + * @see BpmUserTaskAssignStartUserHandlerTypeEnum */ public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { if (assignStartUserHandlerType == null) { @@ -210,9 +210,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人为空时,处理类型枚举 * - * @see BpmUserTaskAssignEmptyHandlerTypeEnum * @param emptyHandler 空处理 - * @param userTask 任务节点 + * @param userTask 任务节点 + * @see BpmUserTaskAssignEmptyHandlerTypeEnum */ public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { if (emptyHandler == null) { @@ -256,7 +256,7 @@ public class BpmnModelUtils { * 给节点添加表单字段权限元素 * * @param fieldsPermissions 表单字段权限 - * @param flowElement 节点 + * @param flowElement 节点 */ public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { if (CollUtil.isNotEmpty(fieldsPermissions)) { @@ -267,7 +267,7 @@ public class BpmnModelUtils { /** * 解析表单字段权限 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 表单字段权限 */ @@ -313,7 +313,7 @@ public class BpmnModelUtils { /** * 解析操作按钮设置 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 操作按钮设置 */ @@ -762,9 +762,9 @@ public class BpmnModelUtils { // 情况:StartEvent/EndEvent/UserTask/ServiceTask if (currentElement instanceof StartEvent - || currentElement instanceof EndEvent - || currentElement instanceof UserTask - || currentElement instanceof ServiceTask) { + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { // 添加元素 FlowNode flowNode = (FlowNode) currentElement; resultElements.add(flowNode); @@ -778,10 +778,16 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - //流程第一次发起时,variables条件值一定为空,发生异常会导致后续分支节点无法预测, - || (null != variables && evalConditionExpress(variables, flow.getConditionExpression()))); + SequenceFlow matchSequenceFlow; + //流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 + if (null == variables) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + } else { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -831,7 +837,7 @@ public class BpmnModelUtils { * 计算条件表达式是否为 true 满足条件 * * @param variables 流程实例 - * @param express 条件表达式 + * @param express 条件表达式 * @return 是否满足条件 */ public static boolean evalConditionExpress(Map variables, String express) { From 8651f2f649df6aa6235c1702722e16e29fc6be80 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 16:03:14 +0800 Subject: [PATCH 183/386] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0nodeIds?= =?UTF-8?q?=E5=8F=82=E6=95=B0=EF=BC=8C=E5=8F=AA=E6=A0=A1=E9=AA=8C=E9=A2=84?= =?UTF-8?q?=E6=B5=8B=E8=BD=A8=E8=BF=B9=E4=B8=8B=E7=9A=84=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java index b13ff561fb..f49f7255a3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -21,4 +21,7 @@ public class BpmProcessInstanceCreateReqVO { @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") private Map> startUserSelectAssignees; + @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List nodeIds; + } From 296e6ab3adbc5954cd5bcaf68d04dedf00a2e2bb Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 16:04:52 +0800 Subject: [PATCH 184/386] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E8=8E=B7?= =?UTF-8?q?=E5=BE=97=E5=AE=A1=E6=89=B9=E8=AF=A6=E6=83=85=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=96=B9=E5=BC=8F=E5=92=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=EF=BC=8C=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E4=BD=BF=E7=94=A8get=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=BC=A0=E9=80=92=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/task/BpmProcessInstanceController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 3a847ce4e8..e3db031e15 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -158,11 +158,11 @@ public class BpmProcessInstanceController { return success(true); } - @GetMapping("/get-approval-detail") + @PostMapping("/get-approval-detail") @Operation(summary = "获得审批详情") @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { + public CommonResult getApprovalDetail(@Valid @RequestBody BpmApprovalDetailReqVO reqVO) { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } From 5e277e020f946e7c7886d70196797356f023e052 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 17 Feb 2025 16:11:00 +0800 Subject: [PATCH 185/386] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E5=AE=A1=E6=89=B9=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/dto/BpmProcessInstanceCreateReqDTO.java | 5 +++++ .../service/task/BpmProcessInstanceServiceImpl.java | 13 +++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java index b1ac35366f..344e2712db 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java @@ -41,4 +41,9 @@ public class BpmProcessInstanceCreateReqDTO { */ private Map> startUserSelectAssignees; + /** + * 活动节点列表 createProcessInstance弃用时可移除 + */ + private List nodeIds; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 96aeaf24b8..51da18e67c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -608,7 +608,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, - createReqVO.getStartUserSelectAssignees()); + createReqVO.getStartUserSelectAssignees(), createReqVO.getNodeIds()); } @Override @@ -620,13 +620,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), - createReqDTO.getStartUserSelectAssignees()); + createReqDTO.getStartUserSelectAssignees(),createReqDTO.getNodeIds()); }); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey, - Map> startUserSelectAssignees) { + Map> startUserSelectAssignees, List nodeIds) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -644,7 +644,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService throw exception(PROCESS_INSTANCE_START_USER_CAN_START); } // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(definition, startUserSelectAssignees); + validateStartUserSelectAssignees(definition, startUserSelectAssignees, nodeIds); // 2. 创建流程实例 if (variables == null) { @@ -690,16 +690,17 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private void validateStartUserSelectAssignees(ProcessDefinition definition, - Map> startUserSelectAssignees) { + Map> startUserSelectAssignees, List nodeIds) { // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel); if (CollUtil.isEmpty(tasks)) { return; } + // 2.流程发起时要先获取当前流程的预测走向节点,发起是 // 2. 校验发起人自选审批人的审批人和抄送人是否都配置了 - tasks.forEach(task -> { + tasks.stream().filter(task -> nodeIds == null || nodeIds.contains(task.getId())).forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); From c68ab33dfd8b665ca1dca9d460d36b692feadd55 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 17 Feb 2025 22:13:42 +0800 Subject: [PATCH 186/386] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20fix:=20=E8=A1=A5=E5=85=85=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E8=A7=84=E5=88=99=EF=BC=8C=E6=B5=81=E7=A8=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E5=8F=AA=E6=A0=A1=E9=AA=8C=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 22 +++++++++---------- .../task/BpmProcessInstanceServiceImpl.java | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 12fa1bf6d9..e09a0f94fa 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -32,7 +32,7 @@ import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_P /** * BPMN Model 操作工具类。目前分成三部分: - *

+ * * 1. BPMN 修改 + 解析元素相关的方法 * 2. BPMN 简单查找相关的方法 * 3. BPMN 复杂遍历相关的方法 @@ -105,8 +105,8 @@ public class BpmnModelUtils { * 给节点添加候选人元素 * * @param candidateStrategy 候选人策略 - * @param candidateParam 候选人参数,允许空 - * @param flowElement 节点 + * @param candidateParam 候选人参数,允许空 + * @param flowElement 节点 */ public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, @@ -150,9 +150,9 @@ public class BpmnModelUtils { /** * 解析审批类型 * + * @see BpmUserTaskApproveTypeEnum * @param userTask 任务节点 * @return 审批类型 - * @see BpmUserTaskApproveTypeEnum */ public static Integer parseApproveType(FlowElement userTask) { return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); @@ -162,7 +162,7 @@ public class BpmnModelUtils { * 添加任务拒绝处理元素 * * @param rejectHandler 任务拒绝处理 - * @param userTask 任务节点 + * @param userTask 任务节点 */ public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { if (rejectHandler == null) { @@ -196,9 +196,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 * - * @param assignStartUserHandlerType 发起人处理类型 - * @param userTask 任务节点 * @see BpmUserTaskAssignStartUserHandlerTypeEnum + * @param assignStartUserHandlerType 发起人处理类型 + * @param userTask 任务节点 */ public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { if (assignStartUserHandlerType == null) { @@ -211,7 +211,7 @@ public class BpmnModelUtils { * 给节点添加用户任务的审批人为空时,处理类型枚举 * * @param emptyHandler 空处理 - * @param userTask 任务节点 + * @param userTask 任务节点 * @see BpmUserTaskAssignEmptyHandlerTypeEnum */ public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { @@ -267,7 +267,7 @@ public class BpmnModelUtils { /** * 解析表单字段权限 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 表单字段权限 */ @@ -313,7 +313,7 @@ public class BpmnModelUtils { /** * 解析操作按钮设置 * - * @param bpmnModel bpmnModel 对象 + * @param bpmnModel bpmnModel 对象 * @param flowElementId 元素 ID * @return 操作按钮设置 */ @@ -837,7 +837,7 @@ public class BpmnModelUtils { * 计算条件表达式是否为 true 满足条件 * * @param variables 流程实例 - * @param express 条件表达式 + * @param express 条件表达式 * @return 是否满足条件 */ public static boolean evalConditionExpress(Map variables, String express) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 51da18e67c..716b7e4cc5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -697,9 +697,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (CollUtil.isEmpty(tasks)) { return; } - // 2.流程发起时要先获取当前流程的预测走向节点,发起是 - // 2. 校验发起人自选审批人的审批人和抄送人是否都配置了 + // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 tasks.stream().filter(task -> nodeIds == null || nodeIds.contains(task.getId())).forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { From 3265df7548769975a20eb600b6f0a0a216ae3b7f Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 17 Feb 2025 22:41:15 +0800 Subject: [PATCH 187/386] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20fix:=20=E8=A1=A5=E5=85=85=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E8=A7=84=E5=88=99=EF=BC=8C=E6=B5=81=E7=A8=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E5=8F=AA=E6=A0=A1=E9=AA=8C=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/flowable/core/util/BpmnModelUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index e09a0f94fa..fe81c7849c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -210,9 +210,9 @@ public class BpmnModelUtils { /** * 给节点添加用户任务的审批人为空时,处理类型枚举 * + * @see BpmUserTaskAssignEmptyHandlerTypeEnum * @param emptyHandler 空处理 * @param userTask 任务节点 - * @see BpmUserTaskAssignEmptyHandlerTypeEnum */ public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { if (emptyHandler == null) { @@ -256,7 +256,7 @@ public class BpmnModelUtils { * 给节点添加表单字段权限元素 * * @param fieldsPermissions 表单字段权限 - * @param flowElement 节点 + * @param flowElement 节点 */ public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { if (CollUtil.isNotEmpty(fieldsPermissions)) { @@ -762,9 +762,9 @@ public class BpmnModelUtils { // 情况:StartEvent/EndEvent/UserTask/ServiceTask if (currentElement instanceof StartEvent - || currentElement instanceof EndEvent - || currentElement instanceof UserTask - || currentElement instanceof ServiceTask) { + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { // 添加元素 FlowNode flowNode = (FlowNode) currentElement; resultElements.add(flowNode); From 1f6f00164ac55116fe256543954960260887f532 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 18 Feb 2025 10:00:36 +0800 Subject: [PATCH 188/386] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E9=A2=84=E6=B5=8B=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/admin/task/BpmProcessInstanceController.java | 4 ++-- .../admin/task/vo/instance/BpmApprovalDetailReqVO.java | 3 +++ .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index e3db031e15..3a847ce4e8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -158,11 +158,11 @@ public class BpmProcessInstanceController { return success(true); } - @PostMapping("/get-approval-detail") + @GetMapping("/get-approval-detail") @Operation(summary = "获得审批详情") @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult getApprovalDetail(@Valid @RequestBody BpmApprovalDetailReqVO reqVO) { + public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index 9121f10362..d9ac673514 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -18,6 +18,9 @@ public class BpmApprovalDetailReqVO { @Schema(description = "流程变量") private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 + @Schema(description = "流程变量") + private String processVariablesStr; // 使用场景:同 processDefinitionId,用于流程预测 + @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 716b7e4cc5..6ae08e1060 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -165,6 +166,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 Map processVariables = reqVO.getProcessVariables(); // 流程变量 + if (reqVO.getProcessVariablesStr() != null){ + processVariables = JSONUtil.parseObj(reqVO.getProcessVariablesStr()); + } // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); From 23ed5b780f88ecc171ca7388e75330cf0488d8e3 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 18 Feb 2025 14:13:49 +0800 Subject: [PATCH 189/386] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=8F=82=E6=95=B0=EF=BC=8C=E9=A2=84=E6=B5=8B=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=9C=A8=E5=90=8E=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/task/dto/BpmProcessInstanceCreateReqDTO.java | 5 ----- .../vo/instance/BpmProcessInstanceCreateReqVO.java | 3 --- .../service/task/BpmProcessInstanceServiceImpl.java | 12 ++++++------ 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java index 344e2712db..b1ac35366f 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java @@ -41,9 +41,4 @@ public class BpmProcessInstanceCreateReqDTO { */ private Map> startUserSelectAssignees; - /** - * 活动节点列表 createProcessInstance弃用时可移除 - */ - private List nodeIds; - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java index f49f7255a3..b13ff561fb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -21,7 +21,4 @@ public class BpmProcessInstanceCreateReqVO { @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") private Map> startUserSelectAssignees; - @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List nodeIds; - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 6ae08e1060..b17aaa4101 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -612,7 +612,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, - createReqVO.getStartUserSelectAssignees(), createReqVO.getNodeIds()); + createReqVO.getStartUserSelectAssignees()); } @Override @@ -624,13 +624,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), - createReqDTO.getStartUserSelectAssignees(),createReqDTO.getNodeIds()); + createReqDTO.getStartUserSelectAssignees()); }); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey, - Map> startUserSelectAssignees, List nodeIds) { + Map> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -648,7 +648,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService throw exception(PROCESS_INSTANCE_START_USER_CAN_START); } // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(definition, startUserSelectAssignees, nodeIds); + validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { @@ -694,7 +694,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private void validateStartUserSelectAssignees(ProcessDefinition definition, - Map> startUserSelectAssignees, List nodeIds) { + Map> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel); @@ -703,7 +703,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 - tasks.stream().filter(task -> nodeIds == null || nodeIds.contains(task.getId())).forEach(task -> { + tasks.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); From 9a83515c0547daf922c75459db0f07ce25e4bfb8 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 18 Feb 2025 16:06:51 +0800 Subject: [PATCH 190/386] =?UTF-8?q?fix:=20=E6=B5=81=E7=A8=8B=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D=E7=BD=AE=E5=9C=A8=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index b17aaa4101..66e615794e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -30,6 +30,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.BpmTaskCandidateStartUserSelectStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; @@ -648,7 +649,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService throw exception(PROCESS_INSTANCE_START_USER_CAN_START); } // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(definition, startUserSelectAssignees); + validateStartUserSelectAssignees(userId, definition, startUserSelectAssignees, variables); // 2. 创建流程实例 if (variables == null) { @@ -693,17 +694,21 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return instance.getId(); } - private void validateStartUserSelectAssignees(ProcessDefinition definition, - Map> startUserSelectAssignees) { - // 1. 获得发起人自选审批人的 UserTask/ServiceTask 列表 - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); - List tasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectTaskList(bpmnModel); - if (CollUtil.isEmpty(tasks)) { + private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, + Map> startUserSelectAssignees, Map variables) { + // 1.获取预测的节点信息 + BpmApprovalDetailReqVO detailReqVO = new BpmApprovalDetailReqVO(); + detailReqVO.setProcessVariables(variables); + detailReqVO.setProcessDefinitionId(definition.getId()); + BpmApprovalDetailRespVO respVO = getApprovalDetail(userId, detailReqVO); + List activityNodes = respVO.getActivityNodes(); + if (CollUtil.isEmpty(activityNodes)){ return; } - + //移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> null == task.getCandidateStrategy() || !task.getCandidateStrategy().equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 - tasks.forEach(task -> { + activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); From 3017933710ad1f930267ae26c1e62786678ab77c Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Wed, 19 Feb 2025 10:04:05 +0800 Subject: [PATCH 191/386] =?UTF-8?q?fix:=20Simple=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=8A=84=E9=80=81=E8=8A=82=E7=82=B9=E5=9C=A8=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E9=A2=84=E6=B5=8B=E6=97=B6=E6=9C=AA=E6=98=BE=E7=A4=BA=E6=8A=84?= =?UTF-8?q?=E9=80=81=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index dc6788a9e2..d786ba2f8f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -499,6 +499,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3. 抄送节点 if (CollUtil.isEmpty(runActivityIds) && // 流程发起时:需要展示抄送节点,用于选择抄送人 BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + activityNode.setCandidateUserIds(candidateUserIds); return activityNode; } return null; From 009f1889a93e7c9e30798b30396e87e853cf0a13 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 12:55:17 +0800 Subject: [PATCH 192/386] =?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?=EF=BC=8C=E4=BF=AE=E6=94=B9=E8=8A=82=E7=82=B9=E7=9A=84=E8=AF=84?= =?UTF-8?q?=E5=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/trigger/BpmFormUpdateTrigger.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java index 6a007c0dfe..0382397d08 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java @@ -49,12 +49,14 @@ public class BpmFormUpdateTrigger implements BpmTrigger { if (CollUtil.isEmpty(setting.getUpdateFormFields())) { continue; } + // 配置了条件,判断条件是否满足 boolean isFormUpdateNeeded = true; - if (setting.getConditionType() != null) { // 配置了条件,判断条件是否满足 - String conditionExpression = SimpleModelUtils.buildConditionExpression(setting.getConditionType(), setting.getConditionExpression(), - setting.getConditionGroups()); + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); } + // 更新流程表单 if (isFormUpdateNeeded) { processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); } From cb3467ada2bd8d07a17699e950147b9333896c39 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 14:11:42 +0800 Subject: [PATCH 193/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 6 +++++ .../vo/instance/BpmApprovalDetailReqVO.java | 2 +- .../flowable/core/util/BpmnModelUtils.java | 13 +++------ .../task/BpmProcessInstanceServiceImpl.java | 27 +++++++++---------- 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 3a847ce4e8..8af426e66d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -1,9 +1,12 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task; import cn.hutool.core.collection.CollUtil; +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.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; @@ -163,6 +166,9 @@ public class BpmProcessInstanceController { @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())){ + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(),Map.class)); + } return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index d9ac673514..d93e2fd69b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -19,7 +19,7 @@ public class BpmApprovalDetailReqVO { private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 @Schema(description = "流程变量") - private String processVariablesStr; // 使用场景:同 processDefinitionId,用于流程预测 + private String processVariablesStr; //解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index fe81c7849c..5909c66c9e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,16 +778,9 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow; - //流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - } else { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); - } + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 66e615794e..91d2405b19 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -29,7 +28,6 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; @@ -167,9 +165,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 Map processVariables = reqVO.getProcessVariables(); // 流程变量 - if (reqVO.getProcessVariablesStr() != null){ - processVariables = JSONUtil.parseObj(reqVO.getProcessVariablesStr()); - } // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); @@ -213,8 +208,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 // B,会不会表单权限不一致哈。 BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId()); - - // 3.2 预测未运行节点的审批信息 + // 3.2 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 + if (null == processVariables) { + processVariables = new HashMap<>(); + } + // 3.3 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); @@ -697,17 +695,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, Map> startUserSelectAssignees, Map variables) { // 1.获取预测的节点信息 - BpmApprovalDetailReqVO detailReqVO = new BpmApprovalDetailReqVO(); - detailReqVO.setProcessVariables(variables); - detailReqVO.setProcessDefinitionId(definition.getId()); - BpmApprovalDetailRespVO respVO = getApprovalDetail(userId, detailReqVO); - List activityNodes = respVO.getActivityNodes(); + BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() + .setProcessDefinitionId(definition.getId()) + .setProcessVariables(variables)); + List activityNodes = detailRespVO.getActivityNodes(); if (CollUtil.isEmpty(activityNodes)){ return; } - //移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> null == task.getCandidateStrategy() || !task.getCandidateStrategy().equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); - // 2. 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 + // 2.移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task. getCandidateStrategy())); + // 3.流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { From 4b89f6893634f1015c40d9de961412824d3c6efd Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 14:13:08 +0800 Subject: [PATCH 194/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/vo/instance/BpmApprovalDetailReqVO.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/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index d93e2fd69b..069ec2000e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -19,7 +19,7 @@ public class BpmApprovalDetailReqVO { private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 @Schema(description = "流程变量") - private String processVariablesStr; //解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 + private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID From 29a902f37ee7eede247527555c2d33b37a571763 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 15:00:28 +0800 Subject: [PATCH 195/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 25 ++++++++++++++----- .../flowable/core/util/SimpleModelUtils.java | 24 +++++++++++++----- .../task/BpmProcessInstanceServiceImpl.java | 6 +---- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 5909c66c9e..edbfbb820d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,9 +778,16 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + SequenceFlow matchSequenceFlow; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 + if (null == variables) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + } else { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -800,9 +807,15 @@ public class BpmnModelUtils { if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); + Collection matchSequenceFlows; + if (null == variables){ + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + }else { + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && evalConditionExpress(variables, flow.getConditionExpression())); + } if (CollUtil.isEmpty(matchSequenceFlows)) { matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e65c88a0f3..910808ef3b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -808,9 +808,15 @@ public class SimpleModelUtils { // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode.getConditionSetting())); + BpmSimpleModelNodeVO matchConditionNode; + if(null == variables) { + matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + }else { + matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + } if (matchConditionNode == null) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -823,9 +829,15 @@ public class SimpleModelUtils { // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode.getConditionSetting())); + Collection matchConditionNodes; + if (null == variables) { + matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + }else { + matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + } if (CollUtil.isEmpty(matchConditionNodes)) { matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 91d2405b19..ff8bb0c4c7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -208,11 +208,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 // B,会不会表单权限不一致哈。 BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId()); - // 3.2 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == processVariables) { - processVariables = new HashMap<>(); - } - // 3.3 预测未运行节点的审批信息 + // 3.2 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); From 642e72ae7abda7264e430cba9593c9e06aee3c28 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 19 Feb 2025 15:02:04 +0800 Subject: [PATCH 196/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/framework/flowable/core/util/BpmnModelUtils.java | 1 + .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index edbfbb820d..d150aad56b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -808,6 +808,7 @@ public class BpmnModelUtils { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; Collection matchSequenceFlows; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 if (null == variables){ matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 910808ef3b..1422d919c0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -809,6 +809,7 @@ public class SimpleModelUtils { if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 BpmSimpleModelNodeVO matchConditionNode; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 if(null == variables) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -830,6 +831,7 @@ public class SimpleModelUtils { if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 Collection matchConditionNodes; + // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 if (null == variables) { matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); From add90365df8d863bbbfe5fa0323032f5b0e4aa4d Mon Sep 17 00:00:00 2001 From: puhui999 Date: Wed, 19 Feb 2025 15:51:34 +0800 Subject: [PATCH 197/386] =?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 248127a941cf511d14c3831d079c64abb805fe44 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 19 Feb 2025 20:01:38 +0800 Subject: [PATCH 198/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 21 +++++-------------- .../flowable/core/util/SimpleModelUtils.java | 18 ++-------------- 2 files changed, 7 insertions(+), 32 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index d150aad56b..49ab5bb35b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,16 +778,9 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - SequenceFlow matchSequenceFlow; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - } else { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); - } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -807,16 +800,9 @@ public class BpmnModelUtils { if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables){ - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - }else { - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); - } if (CollUtil.isEmpty(matchSequenceFlows)) { matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -851,6 +837,9 @@ public class BpmnModelUtils { if (express == null) { return Boolean.FALSE; } + if (variables == null) { + return Boolean.FALSE; + } try { Object result = FlowableUtils.getExpressionValue(variables, express); return Boolean.TRUE.equals(result); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 1422d919c0..98e0c49b69 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -808,16 +808,9 @@ public class SimpleModelUtils { // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - BpmSimpleModelNodeVO matchConditionNode; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if(null == variables) { - matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); - }else { - matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) && evalConditionExpress(variables, conditionNode.getConditionSetting())); - } if (matchConditionNode == null) { matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); @@ -830,16 +823,9 @@ public class SimpleModelUtils { // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 查找满足条件的 BpmSimpleModelNodeVO 节点 - Collection matchConditionNodes; - // 流程首次发起时,variables值一定为空,会导致条件表达式解析错误导致预测节点缺失 - if (null == variables) { - matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); - }else { - matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) && evalConditionExpress(variables, conditionNode.getConditionSetting())); - } if (CollUtil.isEmpty(matchConditionNodes)) { matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); From eaa15b24ff1b521af2103d7041c86242e0af42e7 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 19 Feb 2025 21:05:14 +0800 Subject: [PATCH 199/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.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/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ff8bb0c4c7..e284e42706 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -699,7 +699,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } // 2.移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task. getCandidateStrategy())); + activityNodes.removeIf(task -> !Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 3.流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From 24f1ce16c714dbebc8bd9958f7d05889bfd0226a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 22:20:12 +0800 Subject: [PATCH 200/386] =?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=E4=BC=98=E5=8C=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=8F=91=E8=B5=B7=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=9C=A8=E5=90=8E=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/BpmProcessInstanceController.java | 6 +++--- .../flowable/core/util/BpmnModelUtils.java | 9 ++++++--- .../service/task/BpmProcessInstanceServiceImpl.java | 13 ++++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 8af426e66d..67e7219f1d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.framework.common.util.string.StrUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; @@ -165,9 +164,10 @@ public class BpmProcessInstanceController { @Operation(summary = "获得审批详情") @Parameter(name = "id", description = "流程实例的编号", required = true) @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())){ - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(),Map.class)); + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); } return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 65d38dcbe7..223aa1a7c8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -799,8 +799,8 @@ public class BpmnModelUtils { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); @@ -857,9 +857,12 @@ public class BpmnModelUtils { if (express == null) { return Boolean.FALSE; } + // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables if (variables == null) { - return Boolean.FALSE; + variables = new HashMap<>(); } + + // 执行计算 try { Object result = FlowableUtils.getExpressionValue(variables, express); return Boolean.TRUE.equals(result); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fbe5f97f51..632ed47073 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -696,8 +696,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, - Map> startUserSelectAssignees, Map variables) { - // 1.获取预测的节点信息 + Map> startUserSelectAssignees, + Map variables) { + // 1. 获取预测的节点信息 BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() .setProcessDefinitionId(definition.getId()) .setProcessVariables(variables)); @@ -705,9 +706,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (CollUtil.isEmpty(activityNodes)){ return; } - // 2.移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> !Objects.equals(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); - // 3.流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 + + // 2.1 移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; if (CollUtil.isEmpty(assignees)) { From ceba5b8cecad017bd75ec8ac03ac8154dde32c5a Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 22:35:39 +0800 Subject: [PATCH 201/386] =?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=E4=BC=98=E5=8C=96=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=8F=91=E8=B5=B7=E9=A2=84=E6=B5=8B=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=98=AF=E5=90=A6=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=9C=A8=E5=90=8E=E7=AB=AF=E9=80=BB=E8=BE=91=E4=B8=AD=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/flowable/core/util/BpmnModelUtils.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 223aa1a7c8..27d923bdeb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -850,11 +850,11 @@ public class BpmnModelUtils { * 计算条件表达式是否为 true 满足条件 * * @param variables 流程实例 - * @param express 条件表达式 + * @param expression 条件表达式 * @return 是否满足条件 */ - public static boolean evalConditionExpress(Map variables, String express) { - if (express == null) { + public static boolean evalConditionExpress(Map variables, String expression) { + if (expression == null) { return Boolean.FALSE; } // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables @@ -864,10 +864,11 @@ public class BpmnModelUtils { // 执行计算 try { - Object result = FlowableUtils.getExpressionValue(variables, express); + Object result = FlowableUtils.getExpressionValue(variables, expression); return Boolean.TRUE.equals(result); } catch (FlowableException ex) { - log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", express, variables, ex); + // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 + log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); return Boolean.FALSE; } } From 8e7bbfe0daed7d598c62396d7db0a771bc5768b4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 19 Feb 2025 22:42:19 +0800 Subject: [PATCH 202/386] =?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 203/386] =?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 204/386] =?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 205/386] =?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 0400932260d5ba33f7e813a1daef8be9b0512506 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Thu, 20 Feb 2025 17:44:06 +0800 Subject: [PATCH 206/386] =?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 207/386] =?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 208/386] =?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 209/386] =?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 9698fee36447d3d14a7812c06c6e25f8e4844fb8 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 21 Feb 2025 09:52:43 +0800 Subject: [PATCH 210/386] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmSimpleModelNodeTypeEnum.java | 3 + .../module/bpm/enums/task/BpmReasonEnum.java | 1 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 32 +++++++++ .../core/enums/BpmnVariableConstants.java | 7 ++ .../flowable/core/util/SimpleModelUtils.java | 65 ++++++++++++++++++- .../bpm/service/task/BpmTaskServiceImpl.java | 37 +++++++---- 6 files changed, 132 insertions(+), 13 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index c9da0718df..44cba2bd15 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -30,6 +30,9 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), + CHILD_PROCESS(20, "子流程", "callActivity"), + ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), + // 50 ~ 条件分支 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java index d41c0c3133..bb6b244b02 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java @@ -26,6 +26,7 @@ public enum BpmReasonEnum { TIMEOUT_REJECT("审批超时,系统自动不通过"), ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 50c45a96a2..23371b19d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -10,6 +10,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; +import org.flowable.bpmn.model.IOParameter; import org.hibernate.validator.constraints.URL; import java.util.List; @@ -122,6 +123,8 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; + private ChildProcessSetting childProcessSetting; + @Schema(description = "任务监听器") @Valid @Data @@ -397,4 +400,33 @@ public class BpmSimpleModelNodeVO { private Map updateFormFields; } } + + @Schema(description = "子流程节点配置") + @Data + @Valid + public static class ChildProcessSetting { + + @Schema(description = "被调用流程", example = "xxx") + @NotEmpty(message = "被调用流程不能为空") + private String calledElement; + + @Schema(description = "被调用流程名称", example = "xxx") + @NotEmpty(message = "被调用流程名称不能为空") + private String calledElementName; + + @Schema(description = "是否异步", example = "false") + @NotNull(message = "是否异步不能为空") + private Boolean async; + + @Schema(description = "输入参数(主->子)", example = "[]") + private List inVariable; + + @Schema(description = "输出参数(子->主)", example = "[]") + private List outVariable; + + @Schema(description = "是否自动跳过子流程发起节点", example = "false") + @NotNull(message = "是否自动跳过子流程发起节点不能为空") + private Boolean skipStartUserNode; + + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 8172cf59a5..119a441857 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -51,6 +51,13 @@ public class BpmnVariableConstants { */ public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + /** + * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE"; + /** * 流程实例的变量 - 流程开始时间 * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e32d93ceb0..d41fc44242 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -22,6 +22,8 @@ import org.springframework.util.MultiValueMap; import java.util.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; import static java.util.Arrays.asList; @@ -42,7 +44,8 @@ public class SimpleModelUtils { List converts = asList(new StartNodeConvert(), new EndNodeConvert(), new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), new DelayTimerNodeConvert(), new TriggerNodeConvert(), - new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); + new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), + new ChildProcessConvert(), new AsyncChildProcessConvert()); converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); } @@ -773,6 +776,66 @@ public class SimpleModelUtils { } + private static class ChildProcessConvert implements NodeConvert { + + @Override + public CallActivity convert(BpmSimpleModelNodeVO node) { + BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); + List inVariable = childProcessSetting.getInVariable() == null ? + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable()); + CallActivity callActivity = new CallActivity(); + callActivity.setId(node.getId()); + callActivity.setName(node.getName()); + callActivity.setCalledElementType("key"); + // 1. 是否异步 + callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); + // 2. 调用的子流程 + callActivity.setCalledElement(childProcessSetting.getCalledElement()); + callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); + // 3. 是否自动跳过子流程发起节点 + if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) { + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression("true"); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariable.add(ioParameter); + } else { + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression("false"); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariable.add(ioParameter); + } + // 4. 主→子变量传递 + // 默认需要传递的一些变量 + // 4.1 流程状态 + IOParameter ioParameter = new IOParameter(); + ioParameter.setSource(PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_STATUS); + inVariable.add(ioParameter); + callActivity.setInParameters(inVariable); + // 5. 子→主变量传递 + if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { + callActivity.setOutParameters(childProcessSetting.getOutVariable()); + } + return callActivity; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS; + } + + } + + private static class AsyncChildProcessConvert extends ChildProcessConvert { + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS; + } + + } + + private static String buildGatewayJoinId(String id) { return id + "_join"; } 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 f57f088ef5..e104141965 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 @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.*; import cn.hutool.extra.spring.SpringUtil; @@ -63,6 +64,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; /** @@ -1212,19 +1214,30 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } - // 审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 - if (StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { - // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 - Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + // 发起人节点 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); + return; + } + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 + // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), + PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); + if (userTaskElement.getId().equals(START_USER_NODE_ID) && + (skipStartUserNodeFlag == null || Boolean.TRUE.equals(skipStartUserNodeFlag)) && + !Boolean.TRUE.equals(returnTaskFlag)) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); + return; + } + // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 + if (!userTaskElement.getId().equals(START_USER_NODE_ID) && + StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); - if (bpmnModel == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); - return; - } - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); // 情况一:自动跳过 From 0e48458ef7e85bb6bc8ba5e663885b32164a59f8 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 21 Feb 2025 14:04:00 +0800 Subject: [PATCH 211/386] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B-?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=AB=98=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/SimpleModelUtils.java | 4 ++++ .../task/BpmProcessInstanceServiceImpl.java | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d41fc44242..18798bb3fb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -816,6 +816,8 @@ public class SimpleModelUtils { if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { callActivity.setOutParameters(childProcessSetting.getOutVariable()); } + // 添加节点类型 + addNodeType(node.getType(), callActivity); return callActivity; } @@ -865,6 +867,8 @@ public class SimpleModelUtils { || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS + || nodeType == BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { // 添加元素 resultNodes.add(currentNode); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 632ed47073..c0c1525517 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -320,6 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程 List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -390,7 +391,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List tasks) { // 构建运行中的任务,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null - && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER))); + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); Map> runningTaskMap = convertMultiMap(runActivities, HistoricActivityInstance::getActivityId); @@ -404,7 +405,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) .setName(firstActivity.getActivityName()) - .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”和"子流程"的识别 BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) @@ -413,6 +414,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); + if (task == null) { + continue; + } activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)); // 加签子任务,需要过滤掉已经完成的加签子任务 List childrenTasks = filterList( @@ -503,6 +507,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService activityNode.setCandidateUserIds(candidateUserIds); return activityNode; } + + // 4. 子流程节点 + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType()) || + BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS.getType().equals(node.getType())) { + return activityNode; + } return null; } From 11858ca0dd3b7063ea7743bb551c1ca73fda5089 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 21 Feb 2025 15:40:23 +0800 Subject: [PATCH 212/386] =?UTF-8?q?feat:=20=E5=AD=90=E6=B5=81=E7=A8=8B-?= =?UTF-8?q?=E5=AD=90=E6=B5=81=E7=A8=8B=E5=8F=91=E8=B5=B7=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...BpmChildProcessStartUserEmptyTypeEnum.java | 36 ++++++++ .../BpmChildProcessStartUserTypeEnum.java | 35 ++++++++ .../vo/model/simple/BpmSimpleModelNodeVO.java | 22 +++++ .../flowable/core/util/FlowableUtils.java | 3 +- .../flowable/core/util/SimpleModelUtils.java | 14 +++ .../listener/BpmCallActivityListener.java | 88 +++++++++++++++++++ 6 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java create mode 100644 yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java new file mode 100644 index 0000000000..55e6e02e87 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 当子流程发起人为空时类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserEmptyTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + CHILD_PROCESS_ADMIN(2, "子流程管理员"), + MAIN_PROCESS_ADMIN(3, "主流程管理员"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserEmptyTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserEmptyTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java new file mode 100644 index 0000000000..10d04ea4f4 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程发起人类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + FROM_FORM(2, "表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 23371b19d7..fc1cc2bb4a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -428,5 +428,27 @@ public class BpmSimpleModelNodeVO { @NotNull(message = "是否自动跳过子流程发起节点不能为空") private Boolean skipStartUserNode; + @Schema(description = "子流程发起人配置", example = "{}") + private StartUserSetting startUserSetting; + + @Schema(description = "子流程发起人配置") + @Data + @Valid + public static class StartUserSetting { + + @Schema(description = "子流程发起人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "子流程发起人类型") + @InEnum(BpmChildProcessStartUserTypeEnum.class) + private Integer type; + + @Schema(description = "表单", example = "xxx") + private String formField; + + @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) + private Integer emptyHandleType; + + } + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index a458567d81..998320eb58 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -240,7 +241,7 @@ public class FlowableUtils { return formFieldsMap.entrySet().stream() .limit(3) .map(entry -> new KeyValue<>(entry.getValue().getTitle(), - processVariables.getOrDefault(entry.getValue().getField(), "").toString())) + StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), "")))) .collect(Collectors.toList()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 18798bb3fb..fbc38cbeab 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.*; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; import cn.iocoder.yudao.module.bpm.enums.definition.*; @@ -16,6 +17,7 @@ import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; +import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.delegate.TaskListener; import org.springframework.util.MultiValueMap; @@ -816,6 +818,18 @@ public class SimpleModelUtils { if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { callActivity.setOutParameters(childProcessSetting.getOutVariable()); } + // 6. 子流程发起人配置 + List executionListeners = new ArrayList<>(); + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(ExecutionListener.EVENTNAME_START); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation("${bpmCallActivityListener}"); + FieldExtension fieldExtension = new FieldExtension(); + fieldExtension.setFieldName("listenerConfig"); + fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); + flowableListener.getFieldExtensions().add(fieldExtension); + executionListeners.add(flowableListener); + callActivity.setExecutionListeners(executionListeners); // 添加节点类型 addNodeType(node.getType(), callActivity); return callActivity; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java new file mode 100644 index 0000000000..fe34c6964f --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.bpm.service.task.listener; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserEmptyTypeEnum; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; +import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; +import jakarta.annotation.Resource; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; +import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * BPM 子流程监听器 + * + * @author Lesan + */ +@Component +@Slf4j +public class BpmCallActivityListener implements ExecutionListener { + + public static final String DELEGATE_EXPRESSION = "${bpmCallActivityListener}"; + + @Setter + private FixedValue listenerConfig; + + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Override + public void notify(DelegateExecution execution) { + String expressionText = listenerConfig.getExpressionText(); + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); + BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + // 1. 当发起人来源为表单时 + if (startUserSetting != null && + startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + ExecutionEntity parent = (ExecutionEntity) execution.getParent(); + String formField = parent.getVariable(startUserSetting.getFormField(), String.class); + // 1.1 当表单值为空时 + if (StrUtil.isEmpty(formField)) { + // 1.1.1 来自主流程发起人 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())){ + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + return; + } + // 1.1.2 来自子流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())){ + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + return; + } + // 1.1.3 来自主流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())){ + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + return; + } + } + // 1.2 使用表单值,并兜底字符串转Long失败时使用主流程发起人 + try { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(formField)); + } catch (Exception e) { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + } + } + // 2. 当发起人来源为主流程发起人时,并兜底startUserSetting为空时 + if (startUserSetting == null || + startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { + ExecutionEntity parent = (ExecutionEntity) execution.getParent(); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + } + } + +} \ No newline at end of file From cbdc081cfe348bae4728eab704b96eb30d21617d Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Fri, 21 Feb 2025 22:41:49 +0800 Subject: [PATCH 213/386] =?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=E5=88=A0=E9=99=A4=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../enums/definition/BpmTriggerTypeEnum.java | 3 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 6 +- .../task/BpmProcessInstanceService.java | 26 ++++--- .../task/BpmProcessInstanceServiceImpl.java | 16 +++-- .../task/trigger/BpmFormDeleteTrigger.java | 72 +++++++++++++++++++ 5 files changed, 106 insertions(+), 17 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 42d8bafabb..8944528c06 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -17,7 +17,8 @@ import java.util.Arrays; public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), - FORM_UPDATE(2, "更新流程表单"); + FORM_UPDATE(2, "更新流程表单数据"), + FORM_DELETE(3, "删除流程表单数据"); /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 50c45a96a2..0f4ac41a04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -14,6 +14,7 @@ import org.hibernate.validator.constraints.URL; import java.util.List; import java.util.Map; +import java.util.Set; @Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO") @Data @@ -393,8 +394,11 @@ public class BpmSimpleModelNodeVO { @Schema(description = "条件组", example = "{}") private ConditionGroups conditionGroups; - @Schema(description = "修改的表单字段", example = "userName") + @Schema(description = "修改的表单字段", example = "{}") private Map updateFormFields; + + @Schema(description = "删除表单字段", example = "[]") + private Set deleteFields; } } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 55b86a5514..83d453ed4e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -7,6 +7,7 @@ import jakarta.validation.Valid; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.runtime.ProcessInstance; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -148,15 +149,6 @@ public interface BpmProcessInstanceService { */ void updateProcessInstanceReject(ProcessInstance processInstance, String reason); - // ========== Event 事件相关方法 ========== - - /** - * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 - * - * @param instance 流程任务 - */ - void processProcessInstanceCompleted(ProcessInstance instance); - /** * 更新 ProcessInstance 的变量 * @@ -165,4 +157,20 @@ public interface BpmProcessInstanceService { */ void updateProcessInstanceVariables(String id, Map variables); + /** + * 删除 ProcessInstance 的变量 + * + * @param processInstanceId 流程编号 + * @param variableNames 流程变量名 + */ + void removeProcessInstanceVariables(String processInstanceId, Collection variableNames); + + // ========== Event 事件相关方法 ========== + + /** + * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 + * + * @param instance 流程任务 + */ + void processProcessInstanceCompleted(ProcessInstance instance); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index dc6788a9e2..cc314d347a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -776,6 +776,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmReasonEnum.REJECT_TASK.format(reason)); } + @Override + public void updateProcessInstanceVariables(String id, Map variables) { + runtimeService.setVariables(id, variables); + } + + @Override + public void removeProcessInstanceVariables(String processInstanceId, Collection variableNames) { + runtimeService.removeVariables(processInstanceId, variableNames); + } + // ========== Event 事件相关方法 ========== @Override @@ -809,10 +819,4 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); }); } - - @Override - public void updateProcessInstanceVariables(String id, Map variables) { - runtimeService.setVariables(id, variables); - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java new file mode 100644 index 0000000000..488c48b082 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.bpm.service.task.trigger; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * BPM 删除流程表单数据触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormDeleteTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_DELETE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析删除流程表单数据配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 删除流程表单数据触发器配置为空]", processInstanceId); + return; + } + + // 2.获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3.获取需要删除的表单字段 + Set deleteFields = new HashSet<>(); + settings.forEach(setting -> { + if (CollUtil.isEmpty(setting.getDeleteFields())) { + return; + } + // 配置了条件,判断条件是否满足 + boolean isFieldDeletedNeeded = true; + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); + isFieldDeletedNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + if (isFieldDeletedNeeded) { + deleteFields.addAll(setting.getDeleteFields()); + } + }); + + // 4. 删除流程变量 + if (CollUtil.isNotEmpty(deleteFields)) { + processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); + } + } +} From 7a6d1bdd791cc84f4746d6a096b6d0c7dc28bb2b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 16:20:04 +0800 Subject: [PATCH 214/386] =?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=E5=88=A0=E9=99=A4=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E6=95=B0=E6=8D=AE=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceService.java | 4 ++-- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++-- .../bpm/service/task/trigger/BpmFormDeleteTrigger.java | 6 +++--- .../bpm/service/task/trigger/BpmFormUpdateTrigger.java | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 83d453ed4e..c4684d3402 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -160,10 +160,10 @@ public interface BpmProcessInstanceService { /** * 删除 ProcessInstance 的变量 * - * @param processInstanceId 流程编号 + * @param id 流程编号 * @param variableNames 流程变量名 */ - void removeProcessInstanceVariables(String processInstanceId, Collection variableNames); + void removeProcessInstanceVariables(String id, Collection variableNames); // ========== Event 事件相关方法 ========== diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 271561eeaf..28993d40d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -790,8 +790,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override - public void removeProcessInstanceVariables(String processInstanceId, Collection variableNames) { - runtimeService.removeVariables(processInstanceId, variableNames); + public void removeProcessInstanceVariables(String id, Collection variableNames) { + runtimeService.removeVariables(id, variableNames); } // ========== Event 事件相关方法 ========== diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java index 488c48b082..ab29c502d5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormDeleteTrigger.java @@ -43,10 +43,10 @@ public class BpmFormDeleteTrigger implements BpmTrigger { return; } - // 2.获取流程变量 + // 2. 获取流程变量 Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); - // 3.获取需要删除的表单字段 + // 3.1 获取需要删除的表单字段 Set deleteFields = new HashSet<>(); settings.forEach(setting -> { if (CollUtil.isEmpty(setting.getDeleteFields())) { @@ -64,7 +64,7 @@ public class BpmFormDeleteTrigger implements BpmTrigger { } }); - // 4. 删除流程变量 + // 3.2 删除流程变量 if (CollUtil.isNotEmpty(deleteFields)) { processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java index 0382397d08..9ec2608b93 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/BpmFormUpdateTrigger.java @@ -41,10 +41,10 @@ public class BpmFormUpdateTrigger implements BpmTrigger { return; } - // 2.获取流程变量 + // 2. 获取流程变量 Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); - // 3.更新流程变量 + // 3. 更新流程变量 for (FormTriggerSetting setting : settings) { if (CollUtil.isEmpty(setting.getUpdateFormFields())) { continue; 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 215/386] =?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 254b55778f839de4639c4831ca0110f89bbbaf36 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 17:17:27 +0800 Subject: [PATCH 216/386] =?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=E5=AD=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmSimpleModelNodeTypeEnum.java | 2 +- .../module/bpm/enums/task/BpmReasonEnum.java | 2 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 19 +++++-- .../flowable/core/util/FlowableUtils.java | 6 ++- .../flowable/core/util/SimpleModelUtils.java | 36 ++++++++----- .../task/BpmProcessInstanceServiceImpl.java | 5 +- .../bpm/service/task/BpmTaskServiceImpl.java | 13 ++--- .../listener/BpmCallActivityListener.java | 53 ++++++++++--------- 8 files changed, 81 insertions(+), 55 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java index 44cba2bd15..b21bc65313 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -30,7 +30,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), TRIGGER_NODE(15, "触发器", "serviceTask"), - CHILD_PROCESS(20, "子流程", "callActivity"), + CHILD_PROCESS(20, "子流程", "callActivity"), // TODO @lesan:CHILD_PROCESS、ASYNC_CHILD_PROCESS 可以合并为一个么? ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), // 50 ~ 条件分支 diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java index bb6b244b02..b0ade75299 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java @@ -26,7 +26,7 @@ public enum BpmReasonEnum { TIMEOUT_REJECT("审批超时,系统自动不通过"), ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), - ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), // 目前仅“子流程”使用 ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index b238779a48..9ba95d87e3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -124,6 +124,9 @@ public class BpmSimpleModelNodeVO { */ private TriggerSetting triggerSetting; + /** + * 子流程设置 + */ private ChildProcessSetting childProcessSetting; @Schema(description = "任务监听器") @@ -410,21 +413,24 @@ public class BpmSimpleModelNodeVO { @Valid public static class ChildProcessSetting { - @Schema(description = "被调用流程", example = "xxx") + // TODO @lesan:calledElement => calledProcessDefinitionKey ? 这样更容易理解?不过如果一个流程多次发起,key 变了,好像会有问题? + @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") @NotEmpty(message = "被调用流程不能为空") private String calledElement; - @Schema(description = "被调用流程名称", example = "xxx") + @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") @NotEmpty(message = "被调用流程名称不能为空") private String calledElementName; - @Schema(description = "是否异步", example = "false") + @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") @NotNull(message = "是否异步不能为空") private Boolean async; - @Schema(description = "输入参数(主->子)", example = "[]") + // TODO @lesan:inVariables + @Schema(description = "输入参数(主->子)", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]") private List inVariable; + // TODO @lesan:outVariables @Schema(description = "输出参数(子->主)", example = "[]") private List outVariable; @@ -432,7 +438,8 @@ public class BpmSimpleModelNodeVO { @NotNull(message = "是否自动跳过子流程发起节点不能为空") private Boolean skipStartUserNode; - @Schema(description = "子流程发起人配置", example = "{}") + @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + // TODO @lesan:这个应该也必须填写? private StartUserSetting startUserSetting; @Schema(description = "子流程发起人配置") @@ -448,7 +455,9 @@ public class BpmSimpleModelNodeVO { @Schema(description = "表单", example = "xxx") private String formField; + // TODO @lesan:emptyHandleType => emptyType,和 type 对上? @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "当子流程发起人为空时类型不能为空") @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) private Integer emptyHandleType; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 998320eb58..7ab71a0470 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -25,7 +25,10 @@ import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.TaskInfo; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.Callable; import java.util.stream.Collectors; @@ -241,6 +244,7 @@ public class FlowableUtils { return formFieldsMap.entrySet().stream() .limit(3) .map(entry -> new KeyValue<>(entry.getValue().getTitle(), + // TODO @lesan: MapUtil.getStr 可以更简单? StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), "")))) .collect(Collectors.toList()); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index fbc38cbeab..a92ab7044e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -11,8 +11,11 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.B import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; +import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener; +import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener; import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.constants.BpmnXMLConstants; import org.flowable.bpmn.model.Process; @@ -24,10 +27,7 @@ import org.springframework.util.MultiValueMap; import java.util.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; -import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; import static java.util.Arrays.asList; /** @@ -462,7 +462,7 @@ public class SimpleModelUtils { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(DELEGATE_EXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); addListenerConfig(flowableListener, node.getTaskCreateListener()); flowableListeners.add(flowableListener); } @@ -471,7 +471,7 @@ public class SimpleModelUtils { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(DELEGATE_EXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); addListenerConfig(flowableListener, node.getTaskAssignListener()); flowableListeners.add(flowableListener); } @@ -480,7 +480,7 @@ public class SimpleModelUtils { FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(DELEGATE_EXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); addListenerConfig(flowableListener, node.getTaskCompleteListener()); flowableListeners.add(flowableListener); } @@ -788,48 +788,56 @@ public class SimpleModelUtils { CallActivity callActivity = new CallActivity(); callActivity.setId(node.getId()); callActivity.setName(node.getName()); - callActivity.setCalledElementType("key"); + callActivity.setCalledElementType("key"); // TODO @lesan:这里为啥是 key 哈? // 1. 是否异步 callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); + // 2. 调用的子流程 callActivity.setCalledElement(childProcessSetting.getCalledElement()); callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); + // 3. 是否自动跳过子流程发起节点 + // TODO @lesan:貌似只有 SourceExpression 的区别,直接通过 valueOf childProcessSetting.getSkipStartUserNode()??? if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) { IOParameter ioParameter = new IOParameter(); ioParameter.setSourceExpression("true"); - ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); inVariable.add(ioParameter); } else { IOParameter ioParameter = new IOParameter(); ioParameter.setSourceExpression("false"); - ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); inVariable.add(ioParameter); } + // 4. 主→子变量传递 - // 默认需要传递的一些变量 - // 4.1 流程状态 + // 4.1 【默认需要传递的一些变量】流程状态 + // TODO @lesan:4.1 这个要不,单独一个序号,类似 3. 这个。然后下面,就是把 主→子变量传递、子→主变量传递;这样逻辑连贯点哈 IOParameter ioParameter = new IOParameter(); - ioParameter.setSource(PROCESS_INSTANCE_VARIABLE_STATUS); - ioParameter.setTarget(PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); inVariable.add(ioParameter); callActivity.setInParameters(inVariable); + // 5. 子→主变量传递 + // TODO @lesan:通过 isNotEmpty 这种哈 if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) { callActivity.setOutParameters(childProcessSetting.getOutVariable()); } + // 6. 子流程发起人配置 List executionListeners = new ArrayList<>(); FlowableListener flowableListener = new FlowableListener(); flowableListener.setEvent(ExecutionListener.EVENTNAME_START); flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation("${bpmCallActivityListener}"); + flowableListener.setImplementation(BpmCallActivityListener.DELEGATE_EXPRESSION); FieldExtension fieldExtension = new FieldExtension(); fieldExtension.setFieldName("listenerConfig"); fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); flowableListener.getFieldExtensions().add(fieldExtension); executionListeners.add(flowableListener); callActivity.setExecutionListeners(executionListeners); + // 添加节点类型 addNodeType(node.getType(), callActivity); return callActivity; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 758d63a79e..558180dd3f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -320,7 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会有啥影响?微信聊? List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -389,7 +389,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Map processVariables, List activities, List tasks) { - // 构建运行中的任务,基于 activityId 分组 + // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); Map> runningTaskMap = convertMultiMap(runActivities, @@ -414,6 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); + // TODO @lesan:这里为啥 continue 哈? if (task == null) { continue; } 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 e104141965..902ce8529d 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 @@ -1214,7 +1214,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } - // 发起人节点 + // 获取发起人节点 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); if (bpmnModel == null) { log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); @@ -1227,16 +1227,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); - if (userTaskElement.getId().equals(START_USER_NODE_ID) && - (skipStartUserNodeFlag == null || Boolean.TRUE.equals(skipStartUserNodeFlag)) && - !Boolean.TRUE.equals(returnTaskFlag)) { + if (userTaskElement.getId().equals(START_USER_NODE_ID) + && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 + || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 + && !Boolean.TRUE.equals(returnTaskFlag)) { // TODO @lesan:ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE) 改成这个有问题么?尽量不用 ! 取反 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); return; } // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 - if (!userTaskElement.getId().equals(START_USER_NODE_ID) && - StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { + if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) + && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java index fe34c6964f..2be4fcc8de 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.module.bpm.service.task.listener; -import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -22,7 +21,7 @@ import org.springframework.stereotype.Component; import java.util.List; /** - * BPM 子流程监听器 + * BPM 子流程监听器:设置流程的发起人 * * @author Lesan */ @@ -42,47 +41,51 @@ public class BpmCallActivityListener implements ExecutionListener { public void notify(DelegateExecution execution) { String expressionText = listenerConfig.getExpressionText(); Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); - BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); - // 1. 当发起人来源为表单时 - if (startUserSetting != null && - startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( + expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + + // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 + if (startUserSetting == null + || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { ExecutionEntity parent = (ExecutionEntity) execution.getParent(); - String formField = parent.getVariable(startUserSetting.getFormField(), String.class); - // 1.1 当表单值为空时 - if (StrUtil.isEmpty(formField)) { - // 1.1.1 来自主流程发起人 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())){ + FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); + return; + } + + // 2. 当发起人来源为表单时 + if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + ExecutionEntity parent = (ExecutionEntity) execution.getParent(); + String formFieldValue = parent.getVariable(startUserSetting.getFormField(), String.class); + // 2.1 当表单值为空时 + if (StrUtil.isEmpty(formFieldValue)) { + // 2.1.1 来自主流程发起人 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); return; } - // 1.1.2 来自子流程管理员 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())){ + // 2.1.2 来自子流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); - FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); return; } - // 1.1.3 来自主流程管理员 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())){ + // 2.1.3 来自主流程管理员 + if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); List managerUserIds = processDefinition.getManagerUserIds(); - FlowableUtils.setAuthenticatedUserId(Convert.toLong(managerUserIds.get(0))); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); return; } } - // 1.2 使用表单值,并兜底字符串转Long失败时使用主流程发起人 + // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 try { - FlowableUtils.setAuthenticatedUserId(Long.parseLong(formField)); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); } catch (Exception e) { + // todo @lesan:打个日志,方便排查 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); } } - // 2. 当发起人来源为主流程发起人时,并兜底startUserSetting为空时 - if (startUserSetting == null || - startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { - ExecutionEntity parent = (ExecutionEntity) execution.getParent(); - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); - } } } \ No newline at end of file From fa40ae1dbd368628a9e8a0ef0b6e0636be61ee45 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Feb 2025 18:21:18 +0800 Subject: [PATCH 217/386] =?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 218/386] =?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 219/386] =?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 13c2d36eeea6455d65c4b1262fccfdf816e0558c Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 26 Feb 2025 17:45:35 +0800 Subject: [PATCH 245/386] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=AE=A1=E6=89=B9=E6=97=B6=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=8C=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=AE=A1=E6=89=B9=E7=9A=84=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../task/BpmProcessInstanceServiceImpl.java | 9 +---- .../bpm/service/task/BpmTaskServiceImpl.java | 36 +++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 7fbc7ba223..e089f33751 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -57,6 +57,7 @@ public interface ErrorCodeConstants { ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); + ErrorCode TASK_START_USER_SELECT_NODE_NOT_EXISTS = new ErrorCode(1_009_004_007, "({})不是下一个执行的流程节点!"); // ========== 动态表单模块 1-009-010-000 ========== ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 27884ae51e..3bd5c1098f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -176,14 +176,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 如果流程变量不为空,则用前端传递的新变量值覆盖历史的流程变量 Map historicVariables = historicProcessInstance.getProcessVariables(); if (null != processVariables) { - // 遍历新变量值,仅更新历史变量中存在的键 - for (Map.Entry entry : processVariables.entrySet()) { - String key = entry.getKey(); - if (historicVariables.containsKey(key)) { - // 如果历史变量中存在该键,则用新值覆盖 - historicVariables.put(key, entry.getValue()); - } - } + historicVariables.putAll(processVariables); } processVariables = historicVariables; } 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 1866ed006f..44c04fb030 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 @@ -10,15 +10,19 @@ 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.object.PageUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailReqVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -56,6 +60,7 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -519,6 +524,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); + // 校验传递的参数中是否存在不是下一个执行的节点 + checkNextActivityNodes(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -536,6 +543,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { handleParentTaskIfSign(task.getParentTaskId()); } + /** + * 校验传递的参数中是否存在不是下一个执行的节点 + * + * @param loginUserId 流程发起人 + * @param processInstanceId 流程实例id + * @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]} + */ + private void checkNextActivityNodes(Long loginUserId, Map variables,String processInstanceId, + Map> nextActivityNodes){ + // 1、查询流程【预测】的全部信息 + BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId, + new BpmApprovalDetailReqVO().setProcessVariables(variables).setProcessInstanceId(processInstanceId)); + // 2、获取预测节点的信息 + List activityNodes = approvalDetail.getActivityNodes(); + if (CollUtil.isNotEmpty(activityNodes)) { + // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 + List notStartActivityNodes = activityNodes.stream().filter(node -> + BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) + && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())).toList(); + // 3、校验传递的参数中是否存在不是下一节点的信息 + for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { + if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { + log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey()); + throw exception(TASK_START_USER_SELECT_NODE_NOT_EXISTS, nextActivityNode.getKey()); + } + } + } + } + /** * 审批通过存在“后加签”的任务。 *

From 3c0b9262d7498891d8934d1c347219661936f597 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 26 Feb 2025 21:34:52 +0800 Subject: [PATCH 246/386] =?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=20HTT?= =?UTF-8?q?P=20=E5=BC=82=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/api/task/BpmProcessTaskApi.java | 2 +- .../module/bpm/enums/definition/BpmTriggerTypeEnum.java | 2 +- .../module/bpm/api/task/BpmProcessInstanceApiImpl.java | 1 + .../yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java | 1 + .../definition/vo/model/simple/BpmSimpleModelNodeVO.java | 1 + .../framework/flowable/core/util/SimpleModelUtils.java | 9 +++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 3 +-- .../task/trigger/http/BpmAsyncHttpRequestTrigger.java | 1 + 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java index a17ab46583..8f6f4258c8 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApi.java @@ -9,7 +9,6 @@ import jakarta.validation.constraints.NotEmpty; */ public interface BpmProcessTaskApi { - /** * 触发流程任务的执行 * @@ -18,4 +17,5 @@ public interface BpmProcessTaskApi { */ void triggerTask(@NotEmpty(message = "流程实例的编号不能为空") String processInstanceId, @NotEmpty(message = "任务 Key 不能为空") String taskDefineKey); + } diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index c17780e1bc..78e7079c86 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -19,7 +19,7 @@ public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), FORM_UPDATE(2, "更新流程表单数据"), FORM_DELETE(3, "删除流程表单数据"), - HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); + HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); // TODO @jason:发起 HTTP 回调 /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java index aa9b15cb6d..5ebb12f0f5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -24,4 +24,5 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { return processInstanceService.createProcessInstance(userId, reqDTO); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java index 7e314ebfb9..d99aa03c6a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessTaskApiImpl.java @@ -21,4 +21,5 @@ public class BpmProcessTaskApiImpl implements BpmProcessTaskApi { public void triggerTask(String processInstanceId, String taskDefineKey) { bpmTaskService.triggerTask(processInstanceId, taskDefineKey); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index c0d2a06672..5dab8309d4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -394,6 +394,7 @@ public class BpmSimpleModelNodeVO { */ @Schema(description = "回调任务 Key", example = "xxx", hidden = true) private String callbackTaskDefineKey; + } @Schema(description = "流程表单触发器设置", example = "{}") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 9a32f4cf1f..d505cedf98 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -743,14 +743,15 @@ public class SimpleModelUtils { // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 if (HTTP_REQUEST_ASYNC.getType().equals(node.getTriggerSetting().getType())) { Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); - String attachNodeId = "Activity_" + IdUtil.fastUUID(); ReceiveTask receiveTask = new ReceiveTask(); - receiveTask.setId(attachNodeId); + receiveTask.setId("Activity_" + IdUtil.fastUUID()); receiveTask.setName("异步 HTTP 请求"); - node.setAttachNodeId(attachNodeId); - node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(attachNodeId); // 设置 callbackTaskDefineKey + node.setAttachNodeId(receiveTask.getId()); flowElements.add(receiveTask); + // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 + node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); } + // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); serviceTask.setId(node.getId()); 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 8352ae226c..483741868e 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 @@ -1340,8 +1340,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { .activityId(taskDefineKey) .singleResult(); if (execution == null) { - log.error("[triggerReceiveTask][processInstanceId({}) activityId({}) 没有找到执行活动]", - processInstanceId, taskDefineKey); + log.error("[triggerTask][processInstanceId({}) activityId({}) 没有找到执行活动]", processInstanceId, taskDefineKey); return; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java index 0949c4c8fe..404f7d24d7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java @@ -10,6 +10,7 @@ import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; +// TODO @jason:BpmHttpCallbackTrigger /** * BPM 发送异步 HTTP 请求触发器 * From e892dcabac41a8f5dca5d3c6069c306590d458a3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Wed, 26 Feb 2025 21:45:47 +0800 Subject: [PATCH 247/386] =?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=E5=AD=90=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmChildProcessMultiInstanceSourceTypeEnum.java | 1 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 4 ++-- .../behavior/BpmParallelMultiInstanceBehavior.java | 2 ++ .../behavior/BpmSequentialMultiInstanceBehavior.java | 2 ++ .../flowable/core/util/SimpleModelUtils.java | 11 ++++++----- .../service/task/BpmProcessInstanceServiceImpl.java | 3 ++- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java index f3cac8ba92..8ab275f572 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -33,4 +33,5 @@ public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable< public Integer[] array() { return ARRAYS; } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 5dab8309d4..d0a49c3669 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -394,7 +394,7 @@ public class BpmSimpleModelNodeVO { */ @Schema(description = "回调任务 Key", example = "xxx", hidden = true) private String callbackTaskDefineKey; - + } @Schema(description = "流程表单触发器设置", example = "{}") @@ -509,7 +509,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @NotNull(message = "完成比例不能为空") - private Integer completeRatio; + private Integer completeRatio; // TODO @lesan:approveRatio 要不这个,和上面保持一致? @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "多实例来源类型不能为空") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java index 84c89fc97b..e63c68e4e8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -48,6 +48,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav */ @Override protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 if (execution.getCurrentFlowElement() instanceof UserTask) { // 第一步,设置 collectionVariable 和 CollectionVariable // 从 execution.getVariable() 读取所有任务处理人的 key @@ -72,6 +73,7 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav return assigneeUserIds.size(); } + // 情况二:CallActivity 节点 if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java index a3ba19c70e..35bba43106 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -41,6 +41,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB */ @Override protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 if (execution.getCurrentFlowElement() instanceof UserTask) { // 第一步,设置 collectionVariable 和 CollectionVariable // 从 execution.getVariable() 读取所有任务处理人的 key @@ -66,6 +67,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB return assigneeUserIds.size(); } + // 情况二:CallActivity 节点 if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d505cedf98..94a4796f42 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -721,6 +721,8 @@ public class SimpleModelUtils { } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), null, null, node.getDelaySetting().getDelayTime()); + } else { + throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting()); } flowElements.add(boundaryEvent); } @@ -751,7 +753,7 @@ public class SimpleModelUtils { // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); } - + // 触发器使用 ServiceTask 来实现 ServiceTask serviceTask = new ServiceTask(); serviceTask.setId(node.getId()); @@ -877,6 +879,7 @@ public class SimpleModelUtils { childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); } +// TODO @lesan:String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getCompleteRatio() / 100D))); callActivity.setLoopCharacteristics(multiInstanceCharacteristics); @@ -901,9 +904,7 @@ public class SimpleModelUtils { } private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type, - String timeDuration, - String timeCycle, - String timeDate) { + String timeDuration, String timeCycle, String timeDate) { // 1.1 定时器边界事件 BoundaryEvent boundaryEvent = new BoundaryEvent(); boundaryEvent.setId("Event-" + IdUtil.fastUUID()); @@ -922,7 +923,7 @@ public class SimpleModelUtils { } boundaryEvent.addEventDefinition(eventDefinition); - // 2.1 添加定时器边界事件类型 + // 2. 添加定时器边界事件类型 addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type); return boundaryEvent; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index c3566c8e95..0bb8a29d80 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -414,7 +414,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 处理每个任务的 tasks 属性 for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); - // ChildProcess 子流程节点仅存在于 activity 中,并且没有自身的 task ,需要跳过执行 + // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 + // TODO @芋艿:后续看看怎么优化! if (task == null) { continue; } From 357f4966d30418128ad6aa501bc96bdf45555e42 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 26 Feb 2025 22:38:31 +0800 Subject: [PATCH 248/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=20=E4=BF=AE=E6=94=B9=E6=96=B9=E6=B3=95=E5=90=8D?= =?UTF-8?q?=E7=A7=B0validateNextAssignees?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 44c04fb030..df682c09f9 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 @@ -525,7 +525,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否存在不是下一个执行的节点 - checkNextActivityNodes(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); + validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -550,7 +550,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param processInstanceId 流程实例id * @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]} */ - private void checkNextActivityNodes(Long loginUserId, Map variables,String processInstanceId, + private void validateNextAssignees(Long loginUserId, Map variables,String processInstanceId, Map> nextActivityNodes){ // 1、查询流程【预测】的全部信息 BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId, @@ -562,7 +562,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { List notStartActivityNodes = activityNodes.stream().filter(node -> BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())).toList(); - // 3、校验传递的参数中是否存在不是下一节点的信息 + // 3、校验传递的参数中是否存在不是下一个节点的信息 for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey()); 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 249/386] =?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 b03025746661e637454ed2a70c94e79e4641b9ae Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 26 Feb 2025 23:24:21 +0800 Subject: [PATCH 250/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=20=E4=BF=AE=E6=94=B9=E6=96=B9=E6=B3=95=E5=90=8D?= =?UTF-8?q?=E7=A7=B0validateNextAssignees?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 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 df682c09f9..236efb4f48 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 @@ -561,7 +561,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 List notStartActivityNodes = activityNodes.stream().filter(node -> BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) - && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus())).toList(); + && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus()) + && CollUtil.isEmpty(node.getCandidateUsers())).toList(); // 3、校验传递的参数中是否存在不是下一个节点的信息 for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { From 0565db2f2d5b09edc4dcf935db7ec384a5d363c4 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Thu, 27 Feb 2025 06:40:02 +0800 Subject: [PATCH 251/386] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9=E3=80=91=20=E5=BC=82=E6=AD=A5=20htt?= =?UTF-8?q?p=20=E8=A7=A6=E5=8F=91=E5=99=A8=E4=BF=AE=E6=94=B9=E4=B8=BA=20Ht?= =?UTF-8?q?tp=20=E5=9B=9E=E8=B0=83=E8=A7=A6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/enums/definition/BpmTriggerTypeEnum.java | 7 ++++--- .../vo/model/simple/BpmSimpleModelNodeVO.java | 4 ++-- .../flowable/core/util/SimpleModelUtils.java | 14 +++++++------- .../module/bpm/service/task/BpmTaskService.java | 3 ++- ...estTrigger.java => BpmHttpCallbackTrigger.java} | 13 ++++++------- 5 files changed, 21 insertions(+), 20 deletions(-) rename yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/{BpmAsyncHttpRequestTrigger.java => BpmHttpCallbackTrigger.java} (79%) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index 78e7079c86..c1aaae3437 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -17,9 +17,10 @@ import java.util.Arrays; public enum BpmTriggerTypeEnum implements ArrayValuable { HTTP_REQUEST(1, "发起 HTTP 请求"), - FORM_UPDATE(2, "更新流程表单数据"), - FORM_DELETE(3, "删除流程表单数据"), - HTTP_REQUEST_ASYNC(4, "发起异步 HTTP 请求"); // TODO @jason:发起 HTTP 回调 + HTTP_CALLBACK(2, "发起 HTTP 回调"), + FORM_UPDATE(10, "更新流程表单数据"), + FORM_DELETE(11, "删除流程表单数据"), + ; /** * 触发器执行动作类型 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 5dab8309d4..5c3950d19a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -128,7 +128,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true @JsonIgnore - private String attachNodeId; // 目前用于触发器节点(异步)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 + private String attachNodeId; // 目前用于触发器节点(HTTP 回调)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 /** * 子流程设置 @@ -390,7 +390,7 @@ public class BpmSimpleModelNodeVO { private List> response; /** - * 异步 Http 请求,需要指定回调任务 Key,用于回调执行 + * Http 回调请求,需要指定回调任务 Key,用于回调执行 */ @Schema(description = "回调任务 Key", example = "xxx", hidden = true) private String callbackTaskDefineKey; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index d505cedf98..0c22dba334 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -25,7 +25,7 @@ import org.springframework.util.MultiValueMap; import java.util.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_REQUEST_ASYNC; +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_CALLBACK; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static java.util.Arrays.asList; @@ -175,7 +175,7 @@ public class SimpleModelUtils { SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); process.addFlowElement(sequenceFlow); } else { - // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(异步) + // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(HTTP 回调) List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); sequenceFlows.forEach(process::addFlowElement); } @@ -735,17 +735,17 @@ public class SimpleModelUtils { public static class TriggerNodeConvert implements NodeConvert { - // TODO @芋艿:【异步】在看看 + // TODO @芋艿:【回调】在看看 @Override public List convertList(BpmSimpleModelNodeVO node) { Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); List flowElements = new ArrayList<>(2); - // 异步 HTTP 请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 - if (HTTP_REQUEST_ASYNC.getType().equals(node.getTriggerSetting().getType())) { - Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 请求设置不能为空"); + // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + if (HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); ReceiveTask receiveTask = new ReceiveTask(); receiveTask.setId("Activity_" + IdUtil.fastUUID()); - receiveTask.setName("异步 HTTP 请求"); + receiveTask.setName("HTTP 回调"); node.setAttachNodeId(receiveTask.getId()); flowElements.add(receiveTask); // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index c38771df43..a40fadba75 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -287,11 +287,12 @@ public interface BpmTaskService { /** * 触发流程任务 (ReceiveTask) 的执行 *

- * 1. Simple 模型异步 HTTP 请求触发器节点的回调,触发流程继续执行 + * 1. Simple 模型 HTTP 回调请求触发器节点的回调,触发流程继续执行 * 2. Simple 模型延迟器节点,到时触发流程继续执行 * * @param processInstanceId 流程示例编号 * @param taskDefineKey 任务 Key */ void triggerTask(String processInstanceId, String taskDefineKey); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java similarity index 79% rename from yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java rename to yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index 404f7d24d7..f081ff82d3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAsyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -10,22 +10,21 @@ import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; -// TODO @jason:BpmHttpCallbackTrigger /** - * BPM 发送异步 HTTP 请求触发器 + * BPM HTTP 回调触发器 * * @author jason */ @Component @Slf4j -public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { +public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { @Resource private BpmProcessInstanceService processInstanceService; @Override public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.HTTP_REQUEST_ASYNC; + return BpmTriggerTypeEnum.HTTP_CALLBACK; } @Override @@ -34,7 +33,7 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class); if (setting == null) { - log.error("[execute][流程({}) HTTP 异步触发器请求配置为空]", processInstanceId); + log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); return; } @@ -43,8 +42,8 @@ public class BpmAsyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); // 2.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - // TODO @芋艿:【异步】在看看 - body.add("callbackId", setting.getCallbackTaskDefineKey()); // 异步请求 callbackId 需要传给被调用方,用于回调执行 + // TODO @芋艿:【回调】在看看 + body.add("callbackId", setting.getCallbackTaskDefineKey()); // 回调请求 callbackId 需要传给被调用方,用于回调执行 // 3. 发起请求 sendHttpRequest(setting.getUrl(), headers, body); From 074146c99133bcbdb5c72a7c8f39767db0d7a2fa Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 27 Feb 2025 09:56:34 +0800 Subject: [PATCH 252/386] =?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=20HTT?= =?UTF-8?q?P=20=E5=BC=82=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/definition/BpmTriggerTypeEnum.java | 5 +++-- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 4 +--- .../service/task/trigger/http/BpmHttpCallbackTrigger.java | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java index c1aaae3437..13f997c7b4 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -16,8 +16,9 @@ import java.util.Arrays; @AllArgsConstructor public enum BpmTriggerTypeEnum implements ArrayValuable { - HTTP_REQUEST(1, "发起 HTTP 请求"), - HTTP_CALLBACK(2, "发起 HTTP 回调"), + HTTP_REQUEST(1, "发起 HTTP 请求"), // BPM => 业务,流程继续执行,无需等待业务 + HTTP_CALLBACK(2, "接收 HTTP 回调"), // BPM => 业务 => BPM,流程卡主,等待业务回调 + FORM_UPDATE(10, "更新流程表单数据"), FORM_DELETE(11, "删除流程表单数据"), ; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 6cff39f4f6..e0911240fc 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -25,7 +25,6 @@ import org.springframework.util.MultiValueMap; import java.util.*; -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum.HTTP_CALLBACK; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; import static java.util.Arrays.asList; @@ -737,13 +736,12 @@ public class SimpleModelUtils { public static class TriggerNodeConvert implements NodeConvert { - // TODO @芋艿:【回调】在看看 @Override public List convertList(BpmSimpleModelNodeVO node) { Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); List flowElements = new ArrayList<>(2); // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 - if (HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { + if (BpmTriggerTypeEnum.HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); ReceiveTask receiveTask = new ReceiveTask(); receiveTask.setId("Activity_" + IdUtil.fastUUID()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index f081ff82d3..867af70b9d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -42,8 +42,8 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); // 2.2 设置请求体 MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - // TODO @芋艿:【回调】在看看 - body.add("callbackId", setting.getCallbackTaskDefineKey()); // 回调请求 callbackId 需要传给被调用方,用于回调执行 + // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 + body.add("taskDefineKey", setting.getCallbackTaskDefineKey()); // 3. 发起请求 sendHttpRequest(setting.getUrl(), headers, body); 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 253/386] =?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 254/386] =?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 255/386] =?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 db6d7a74300da30b721b1e2cdaa7de78f2e810af Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 27 Feb 2025 15:17:30 +0800 Subject: [PATCH 256/386] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0TODO?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/framework/flowable/core/util/BpmnModelUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 49ab5bb35b..b73e0cc98d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -778,6 +778,8 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; + // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题,需要优化, + // TODO 具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); From 6c6992c86a9f64b78e23c622a6f6204cd424e6ef Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 27 Feb 2025 17:28:22 +0800 Subject: [PATCH 257/386] =?UTF-8?q?feat=EF=BC=9ATODO=20List?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskServiceImpl.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) 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 236efb4f48..d64a822545 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 @@ -525,6 +525,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否存在不是下一个执行的节点 + // 当前执行的流程节点,需根据该节点寻找下一个节点 + String taskDefinitionKey = task.getTaskDefinitionKey(); + List nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables); + System.out.println(nextFlowNodes); validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { @@ -543,6 +547,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { handleParentTaskIfSign(task.getParentTaskId()); } + /** + * + * @param taskDefinitionKey 当前节点id + * @param bpmnModel bpmnModel + */ + private List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables){ + FlowNode flowElement = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + // 存储下一个执行的节点 + List nextFlowNodes = new ArrayList<>(); + resolveNextNodes(flowElement, bpmnModel, variables, nextFlowNodes); + return nextFlowNodes; + } + + private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + List outgoingFlows = currentNode.getOutgoingFlows(); + for (SequenceFlow sequenceFlow : outgoingFlows) { + // 如果是排他网关,需要根据条件表达式判断 + if (currentNode instanceof ExclusiveGateway) { + String conditionExpression = sequenceFlow.getConditionExpression(); + if (conditionExpression != null && !BpmnModelUtils.evalConditionExpress(variables,conditionExpression)) { + continue; + } + } + FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode targetNode) { + if (targetNode instanceof Gateway) { + // 如果目标节点还是网关,递归处理 + resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes); + } else { + nextFlowNodes.add(targetNode); + } + } + } + } + /** * 校验传递的参数中是否存在不是下一个执行的节点 * @@ -559,6 +598,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { List activityNodes = approvalDetail.getActivityNodes(); if (CollUtil.isNotEmpty(activityNodes)) { // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 + // TODO 获取下一个执行节点 List notStartActivityNodes = activityNodes.stream().filter(node -> BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus()) 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 258/386] =?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 259/386] =?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 260/386] =?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: Thu, 27 Feb 2025 23:33:16 +0800 Subject: [PATCH 261/386] feat: add TODO --- .../bpm/service/task/BpmTaskServiceImpl.java | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) 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 d64a822545..04842ea51d 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 @@ -529,7 +529,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { String taskDefinitionKey = task.getTaskDefinitionKey(); List nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables); System.out.println(nextFlowNodes); - validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); +// validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -548,40 +548,66 @@ public class BpmTaskServiceImpl implements BpmTaskService { } /** - * - * @param taskDefinitionKey 当前节点id - * @param bpmnModel bpmnModel + * 根据当前节点 ID 获取下一个执行的 FlowNode 列表 + * @param taskDefinitionKey 当前节点 ID + * @param bpmnModel BPMN 模型 + * @param variables 流程变量,用于条件判断 + * @return 下一个执行的 FlowNode 列表 */ - private List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables){ - FlowNode flowElement = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); - // 存储下一个执行的节点 + public List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables) { + if (taskDefinitionKey == null || bpmnModel == null) { + throw new IllegalArgumentException("taskDefinitionKey and bpmnModel cannot be null"); + } + FlowNode currentNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + if (currentNode == null) { + throw new IllegalArgumentException("FlowElement with given taskDefinitionKey not found in BpmnModel"); + } List nextFlowNodes = new ArrayList<>(); - resolveNextNodes(flowElement, bpmnModel, variables, nextFlowNodes); + resolveNextNodes(currentNode, bpmnModel, variables, nextFlowNodes); return nextFlowNodes; } + /** + * 递归解析下一个执行节点 + * @param currentNode 当前节点 + * @param bpmnModel BPMN 模型 + * @param variables 流程变量,用于条件判断 + * @param nextFlowNodes 存储下一个执行节点的列表 + */ private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { List outgoingFlows = currentNode.getOutgoingFlows(); for (SequenceFlow sequenceFlow : outgoingFlows) { - // 如果是排他网关,需要根据条件表达式判断 - if (currentNode instanceof ExclusiveGateway) { - String conditionExpression = sequenceFlow.getConditionExpression(); - if (conditionExpression != null && !BpmnModelUtils.evalConditionExpress(variables,conditionExpression)) { - continue; - } + if (!shouldFollowSequenceFlow(currentNode, sequenceFlow, variables)) { + continue; } FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef()); if (targetElement instanceof FlowNode targetNode) { if (targetNode instanceof Gateway) { - // 如果目标节点还是网关,递归处理 + // 如果目标节点是网关,递归处理 resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes); - } else { + }else { nextFlowNodes.add(targetNode); } } } } + /** + * 判断是否应该遵循当前序列流 + * @param currentNode 当前节点 + * @param sequenceFlow 序列流 + * @param variables 流程变量,用于条件判断 + * @return 是否应该遵循该序列流 + */ + private boolean shouldFollowSequenceFlow(FlowNode currentNode, SequenceFlow sequenceFlow, Map variables) { + if (currentNode instanceof ExclusiveGateway) { + String conditionExpression = sequenceFlow.getConditionExpression(); + return conditionExpression == null || BpmnModelUtils.evalConditionExpress(variables, conditionExpression); + } + return true; + } + + /** * 校验传递的参数中是否存在不是下一个执行的节点 * From 8df3a2d9502a9097d0629fa64b1bc26192357066 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 28 Feb 2025 09:29:56 +0800 Subject: [PATCH 262/386] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmChildProcessMultiInstanceSourceTypeEnum.java | 4 ++-- .../definition/vo/model/simple/BpmSimpleModelNodeVO.java | 2 +- .../core/behavior/BpmParallelMultiInstanceBehavior.java | 4 ++-- .../core/behavior/BpmSequentialMultiInstanceBehavior.java | 4 ++-- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 7 +++---- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java index 8ab275f572..fab0ddfd71 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -17,8 +17,8 @@ import java.util.Arrays; public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable { FIXED_QUANTITY(1, "固定数量"), - DIGITAL_FORM(2, "数字表单"), - MULTI_FORM(3, "多项表单"); + NUMBER_FORM(2, "数字表单"), + MULTIPLE_FORM(3, "多选表单"); private final Integer type; private final String name; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index b18ad23561..f002e6894a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -509,7 +509,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") @NotNull(message = "完成比例不能为空") - private Integer completeRatio; // TODO @lesan:approveRatio 要不这个,和上面保持一致? + private Integer approveRatio; @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "多实例来源类型不能为空") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java index e63c68e4e8..57f4d393f3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -77,10 +77,10 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); } - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java index 35bba43106..cb748182ed 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -71,10 +71,10 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB if (execution.getCurrentFlowElement() instanceof CallActivity) { FlowElement flowElement = execution.getCurrentFlowElement(); Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); } - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e0911240fc..da17b6d901 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -873,13 +873,12 @@ public class SimpleModelUtils { if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) { multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource()); } - if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType()) || - childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) { + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType()) || + childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); } -// TODO @lesan:String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", - String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getCompleteRatio() / 100D))); + String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D)))); callActivity.setLoopCharacteristics(multiInstanceCharacteristics); addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); } From 5b88d88177abeb846dc5b7210a15626be038c2ba Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 28 Feb 2025 09:35:43 +0800 Subject: [PATCH 263/386] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/SimpleModelUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index da17b6d901..0060d0e9c5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -877,8 +877,8 @@ public class SimpleModelUtils { childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); } - multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}", - String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D)))); + multiInstanceCharacteristics.setCompletionCondition(String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), + String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D))); callActivity.setLoopCharacteristics(multiInstanceCharacteristics); addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); } From 69a27b1ee2024510bd4b8c3bb28a014927cdbbcc Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 28 Feb 2025 14:46:02 +0800 Subject: [PATCH 264/386] =?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 265/386] =?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 deef88f56fc4dd1235f24a10c11e1991be73a034 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Fri, 28 Feb 2025 17:10:59 +0800 Subject: [PATCH 266/386] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 119 ++++++++++++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 133 ++++++------------ 2 files changed, 161 insertions(+), 91 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index b73e0cc98d..7873681318 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -828,6 +828,125 @@ public class BpmnModelUtils { } } + /** + * 根据当前节点,获取下一个节点 + * + * @param currentElement 当前节点 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + */ + public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, + Map variables){ + // 下一个执行的流程节点集合 + List nextFlowNodes = new ArrayList<>(); + // 当前执行节点的基本属性 + FlowNode currentNode = (FlowNode) currentElement; + // 获取当前节点的关联节点 + List outgoingFlows = currentNode.getOutgoingFlows(); + if (CollUtil.isEmpty(outgoingFlows)){ + log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); + return nextFlowNodes; + } + // 遍历每个出口流 + for (SequenceFlow outgoingFlow : outgoingFlows) { + // 获取目标节点的基本属性 + FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); + if (targetElement == null){ + continue; + } + if (targetElement instanceof Gateway gateway) { + // 处理不同类型的网关 + if (gateway instanceof ExclusiveGateway) { + handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof InclusiveGateway) { + handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof ParallelGateway) { + handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); + } + } else { + // 如果不是网关,直接添加到下一个节点列表 + nextFlowNodes.add((FlowNode) targetElement); + } + } + return nextFlowNodes; + } + + /** + * 处理排他网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // TODO @小北: 这里findOne和simulateNextFlowElements中有重复代码,需要优化,@芋道:是否重构?? + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + if (matchSequenceFlow == null) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlow = gateway.getOutgoingFlows().get(0); + } + } + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理包容网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && evalConditionExpress(variables, flow.getConditionExpression())); + if (CollUtil.isEmpty(matchSequenceFlows)) { + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlows = gateway.getOutgoingFlows(); + } + } + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + /** + * 处理并行网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // 并行网关,遍历所有出口路径,获取目标节点 + gateway.getOutgoingFlows().forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + /** * 计算条件表达式是否为 true 满足条件 * 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 04842ea51d..1a00f38617 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 @@ -23,6 +23,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -522,14 +523,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - if (CollUtil.isNotEmpty(reqVO.getVariables())) { +// if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否存在不是下一个执行的节点 - // 当前执行的流程节点,需根据该节点寻找下一个节点 - String taskDefinitionKey = task.getTaskDefinitionKey(); - List nextFlowNodes = getNextFlowNodes(taskDefinitionKey, bpmnModel, variables); - System.out.println(nextFlowNodes); -// validateNextAssignees(userId, reqVO.getVariables(), task.getProcessInstanceId(), reqVO.getNextAssignees()); + validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 @@ -539,104 +536,58 @@ public class BpmTaskServiceImpl implements BpmTaskService { } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); - } else { - taskService.complete(task.getId()); - } +// } else { +// taskService.complete(task.getId()); +// } // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); } - /** - * 根据当前节点 ID 获取下一个执行的 FlowNode 列表 - * @param taskDefinitionKey 当前节点 ID - * @param bpmnModel BPMN 模型 - * @param variables 流程变量,用于条件判断 - * @return 下一个执行的 FlowNode 列表 - */ - public List getNextFlowNodes(String taskDefinitionKey, BpmnModel bpmnModel, Map variables) { - if (taskDefinitionKey == null || bpmnModel == null) { - throw new IllegalArgumentException("taskDefinitionKey and bpmnModel cannot be null"); - } - FlowNode currentNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); - if (currentNode == null) { - throw new IllegalArgumentException("FlowElement with given taskDefinitionKey not found in BpmnModel"); - } - List nextFlowNodes = new ArrayList<>(); - resolveNextNodes(currentNode, bpmnModel, variables, nextFlowNodes); - return nextFlowNodes; - } - - /** - * 递归解析下一个执行节点 - * @param currentNode 当前节点 - * @param bpmnModel BPMN 模型 - * @param variables 流程变量,用于条件判断 - * @param nextFlowNodes 存储下一个执行节点的列表 - */ - private void resolveNextNodes(FlowNode currentNode, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - List outgoingFlows = currentNode.getOutgoingFlows(); - for (SequenceFlow sequenceFlow : outgoingFlows) { - if (!shouldFollowSequenceFlow(currentNode, sequenceFlow, variables)) { - continue; - } - FlowElement targetElement = bpmnModel.getFlowElement(sequenceFlow.getTargetRef()); - if (targetElement instanceof FlowNode targetNode) { - if (targetNode instanceof Gateway) { - // 如果目标节点是网关,递归处理 - resolveNextNodes(targetNode, bpmnModel, variables, nextFlowNodes); - }else { - nextFlowNodes.add(targetNode); - } - } - } - } - - /** - * 判断是否应该遵循当前序列流 - * @param currentNode 当前节点 - * @param sequenceFlow 序列流 - * @param variables 流程变量,用于条件判断 - * @return 是否应该遵循该序列流 - */ - private boolean shouldFollowSequenceFlow(FlowNode currentNode, SequenceFlow sequenceFlow, Map variables) { - if (currentNode instanceof ExclusiveGateway) { - String conditionExpression = sequenceFlow.getConditionExpression(); - return conditionExpression == null || BpmnModelUtils.evalConditionExpress(variables, conditionExpression); - } - return true; - } - /** * 校验传递的参数中是否存在不是下一个执行的节点 * - * @param loginUserId 流程发起人 - * @param processInstanceId 流程实例id - * @param nextActivityNodes 下一个执行节点信息 {节点id : [审批人id,审批人id]} + * @param taskDefinitionKey 当前任务节点id + * @param variables 流程变量 + * @param bpmnModel 流程模型 + * @param nextActivityNodes 下一个节点审批人集合(参数) */ - private void validateNextAssignees(Long loginUserId, Map variables,String processInstanceId, - Map> nextActivityNodes){ - // 1、查询流程【预测】的全部信息 - BpmApprovalDetailRespVO approvalDetail = processInstanceService.getApprovalDetail(loginUserId, - new BpmApprovalDetailReqVO().setProcessVariables(variables).setProcessInstanceId(processInstanceId)); - // 2、获取预测节点的信息 - List activityNodes = approvalDetail.getActivityNodes(); - if (CollUtil.isNotEmpty(activityNodes)) { - // 2.1、获取节点中的审批人策略为【发起人自选】且状态为【未执行】的节点 - // TODO 获取下一个执行节点 - List notStartActivityNodes = activityNodes.stream().filter(node -> - BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy().equals(node.getCandidateStrategy()) - && BpmTaskStatusEnum.NOT_START.getStatus().equals(node.getStatus()) - && CollUtil.isEmpty(node.getCandidateUsers())).toList(); - // 3、校验传递的参数中是否存在不是下一个节点的信息 - for (Map.Entry> nextActivityNode : nextActivityNodes.entrySet()) { - if (notStartActivityNodes.stream().noneMatch(taskNode -> taskNode.getId().equals(nextActivityNode.getKey()))) { - log.error("[checkNextActivityNodes][ ({}) 不是下一个执行的流程节点!]", nextActivityNode.getKey()); - throw exception(TASK_START_USER_SELECT_NODE_NOT_EXISTS, nextActivityNode.getKey()); + private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + Map> nextActivityNodes,ProcessInstance processInstance){ + + // 1、获取当前任务节点的信息 + FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); + // 2、获取下一个应该执行的节点集合 + List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + // 3、比较前端传递的节点和预测的下一个节点是否匹配,匹配则将该节点设置上审批人 + for (FlowNode nextFlowNode : nextFlowNodes) { + // 获取下一个执行节点的属性 是否为 发起人自选 + Map> extensionElements = nextFlowNode.getExtensionElements(); + List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); + if (CollUtil.isEmpty(elements)){ + continue; + } + // 获取节点中的审批人策略 + Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); + // 获取流程实例中的发起人自选审批人 + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { + // 先判断节点是否存在 + if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ + throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + } + // 如果节点存在,则判断节点中的审批人策略是否为 发起人自选 + List nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); + // 3.1、如果前端传递的节点为空,则抛出异常 + if (CollUtil.isEmpty(nextAssignees)) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } + } + } /** From 7b449b81e7be2671446a94a74c9c539f6c9be2a8 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 28 Feb 2025 18:03:34 +0800 Subject: [PATCH 267/386] =?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 ff555b51366c1e0df4a571f59a3cef4f812546fb Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Fri, 28 Feb 2025 22:37:57 +0800 Subject: [PATCH 268/386] =?UTF-8?q?feat:=20=E5=AE=A1=E6=89=B9=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E4=B8=BA=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 2 +- .../bpm/service/task/BpmTaskServiceImpl.java | 32 +++++++++---------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 7873681318..fff0ac5317 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -880,7 +880,7 @@ public class BpmnModelUtils { * @param nextFlowNodes 下一个执行的流程节点集合 */ private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里findOne和simulateNextFlowElements中有重复代码,需要优化,@芋道:是否重构?? + // TODO @小北: 这里和simulateNextFlowElements中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋道 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); 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 1a00f38617..bcc70da867 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 @@ -523,9 +523,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 -// if (CollUtil.isNotEmpty(reqVO.getVariables())) { + if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); - // 校验传递的参数中是否存在不是下一个执行的节点 + // 校验传递的参数中是否为下一个将要执行的任务节点 validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 下个节点审批人如果不存在,则由前端传递 if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { @@ -536,9 +536,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { } runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); -// } else { -// taskService.complete(task.getId()); -// } + } else { + taskService.complete(task.getId()); + } // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); @@ -546,7 +546,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { /** - * 校验传递的参数中是否存在不是下一个执行的节点 + * 校验传递的参数中是否为下一个将要执行的任务节点 * * @param taskDefinitionKey 当前任务节点id * @param variables 流程变量 @@ -555,39 +555,37 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextActivityNodes,ProcessInstance processInstance){ - // 1、获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - // 2、获取下一个应该执行的节点集合 + // 2、获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3、比较前端传递的节点和预测的下一个节点是否匹配,匹配则将该节点设置上审批人 + // 3、循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 获取下一个执行节点的属性 是否为 发起人自选 + // 3.1、获取下一个将要执行节点的属性(是否为自选审批人等) Map> extensionElements = nextFlowNode.getExtensionElements(); List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); if (CollUtil.isEmpty(elements)){ continue; } - // 获取节点中的审批人策略 + // 3.2、获取节点中的审批人策略 Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 获取流程实例中的发起人自选审批人 + // 3.3、获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); + // 3.4、如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 先判断节点是否存在 + // 先判断前端传递的参数节点节点是否为将要执行的节点 if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果节点存在,则判断节点中的审批人策略是否为 发起人自选 + // 如果节点存在,则获取节点中的审批人 List nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); - // 3.1、如果前端传递的节点为空,则抛出异常 + // 如果前端传递的节点为空,则抛出异常 if (CollUtil.isEmpty(nextAssignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } - } - } /** From fedb9242b59b9846951d49e475b718c5a87a406e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 16:34:30 +0800 Subject: [PATCH 269/386] =?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=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 - .../task/vo/task/BpmTaskApproveReqVO.java | 2 +- ...mTaskCandidateStartUserSelectStrategy.java | 44 +++------------- .../flowable/core/util/BpmnModelUtils.java | 40 ++++++++------- .../task/BpmProcessInstanceServiceImpl.java | 4 +- .../bpm/service/task/BpmTaskServiceImpl.java | 51 +++++++++++-------- 6 files changed, 62 insertions(+), 80 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index a1e2d45aa9..3a50bba523 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -58,7 +58,6 @@ public interface ErrorCodeConstants { ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); - ErrorCode TASK_START_USER_SELECT_NODE_NOT_EXISTS = new ErrorCode(1_009_004_007, "({})不是下一个执行的流程节点!"); // ========== 动态表单模块 1-009-010-000 ========== ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index a0751c12e6..0969fda135 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -25,6 +25,6 @@ public class BpmTaskApproveReqVO { private Map variables; @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") - private Map> nextAssignees; + private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java index f4efa549e5..9304d288ac 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -2,24 +2,21 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.d import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import com.google.common.collect.Sets; import jakarta.annotation.Resource; import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.ServiceTask; -import org.flowable.bpmn.model.Task; -import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; -import java.util.*; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; /** * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类 @@ -53,12 +50,9 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", execution.getProcessInstanceId()); - // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 + // 获得审批人 List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); - if (CollUtil.isEmpty(assignees)){ - return Sets.newLinkedHashSet(); - } - return new LinkedHashSet<>(assignees); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); } @Override @@ -71,33 +65,9 @@ public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCand if (startUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } - // 获得审批人,如果不存在,则直接返回空,fix: 用于节点预测时,如果该节点不存在发起人自选审批人,类型转换异常 + // 获得审批人 List assignees = startUserSelectAssignees.get(activityId); - if (CollUtil.isEmpty(assignees)){ - return Sets.newLinkedHashSet(); - } - return new LinkedHashSet<>(assignees); - } - - /** - * 获得发起人自选审批人或抄送人的 Task 列表 - * - * @param bpmnModel BPMN 模型 - * @return Task 列表 - */ - public static List getStartUserSelectTaskList(BpmnModel bpmnModel) { - if (bpmnModel == null) { - return Collections.emptyList(); - } - List tasks = new ArrayList<>(); - tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class)); - tasks.addAll(BpmnModelUtils.getBpmnModelElements(bpmnModel, ServiceTask.class)); - if (CollUtil.isEmpty(tasks)) { - return Collections.emptyList(); - } - tasks.removeIf(task -> ObjectUtil.notEqual(BpmnModelUtils.parseCandidateStrategy(task), - BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())); - return tasks; + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 31c27c2b2a..1eea893f76 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -809,8 +809,7 @@ public class BpmnModelUtils { if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 Gateway gateway = (Gateway) currentElement; - // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题,需要优化, - // TODO 具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 + // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题。需要优化,具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -866,27 +865,27 @@ public class BpmnModelUtils { * @param bpmnModel BPMN模型 * @param variables 流程变量 */ + @SuppressWarnings("PatternVariableCanBeUsed") public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, Map variables){ - // 下一个执行的流程节点集合 - List nextFlowNodes = new ArrayList<>(); - // 当前执行节点的基本属性 - FlowNode currentNode = (FlowNode) currentElement; - // 获取当前节点的关联节点 - List outgoingFlows = currentNode.getOutgoingFlows(); - if (CollUtil.isEmpty(outgoingFlows)){ + List nextFlowNodes = new ArrayList<>(); // 下一个执行的流程节点集合 + FlowNode currentNode = (FlowNode) currentElement; // 当前执行节点的基本属性 + List outgoingFlows = currentNode.getOutgoingFlows(); // 当前节点的关联节点 + if (CollUtil.isEmpty(outgoingFlows)) { log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); return nextFlowNodes; } + // 遍历每个出口流 for (SequenceFlow outgoingFlow : outgoingFlows) { // 获取目标节点的基本属性 FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); - if (targetElement == null){ + if (targetElement == null) { continue; } - if (targetElement instanceof Gateway gateway) { - // 处理不同类型的网关 + // 情况一:处理不同类型的网关 + if (targetElement instanceof Gateway) { + Gateway gateway = (Gateway) targetElement; if (gateway instanceof ExclusiveGateway) { handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); } else if (gateway instanceof InclusiveGateway) { @@ -895,7 +894,7 @@ public class BpmnModelUtils { handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); } } else { - // 如果不是网关,直接添加到下一个节点列表 + // 情况二:如果不是网关,直接添加到下一个节点列表 nextFlowNodes.add((FlowNode) targetElement); } } @@ -903,15 +902,17 @@ public class BpmnModelUtils { } /** - * 处理排他网关 + * 处理排它网关 * * @param gateway 排他网关 * @param bpmnModel BPMN模型 * @param variables 流程变量 * @param nextFlowNodes 下一个执行的流程节点集合 */ - private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里和simulateNextFlowElements中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋道 + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // TODO @小北: 这里和 simulateNextFlowElements 中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋艿 + // TODO @小北:ok,把 simulateNextFlowElements 里面处理网关的,复用这个方法,可以么? SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -940,7 +941,8 @@ public class BpmnModelUtils { * @param variables 流程变量 * @param nextFlowNodes 下一个执行的流程节点集合 */ - private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); @@ -960,6 +962,7 @@ public class BpmnModelUtils { } }); } + /** * 处理并行网关 * @@ -968,7 +971,8 @@ public class BpmnModelUtils { * @param variables 流程变量 * @param nextFlowNodes 下一个执行的流程节点集合 */ - private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { // 并行网关,遍历所有出口路径,获取目标节点 gateway.getOutgoingFlows().forEach(flow -> { FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 2b8e67c147..9da2610cb6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -175,9 +175,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); - // 如果流程变量不为空,则用前端传递的新变量值覆盖历史的流程变量 + // 合并 DB 和前端传递的流量变量,以前端的为主 Map historicVariables = historicProcessInstance.getProcessVariables(); - if (null != processVariables) { + if (CollUtil.isNotEmpty(processVariables)) { historicVariables.putAll(processVariables); } processVariables = historicVariables; 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 ab9238dc10..0b5a70ab08 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 @@ -11,13 +11,10 @@ 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.object.PageUtils; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailReqVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.bpm.enums.definition.*; import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; @@ -63,7 +60,6 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.*; -import java.util.stream.Collectors; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -535,9 +531,15 @@ public class BpmTaskServiceImpl implements BpmTaskService { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 下个节点审批人如果不存在,则由前端传递 + // 如果有下一个审批人,则设置到流程变量中 + // TODO @小北:validateNextAssignees 升级成 validateAndSetNextAssignees,然后里面吧下面这一小段逻辑,抽进去如何? if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 + // TODO @小北:这里有个需要讨论的点,微信哈; + // TODO 因为 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES 定位是发起人,那么审批人选择的,放在 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES。目前想到两个方案: + // TODO 方案一:增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,然后设置到这里面。然后,BpmTaskCandidateStartUserSelectStrategy 也从这里读 + // TODO 方案二:也是增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,根据节点审批人类型,放到 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES、PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES + // TODO 方案三:融合成 PROCESS_INSTANCE_VARIABLE_USER_SELECT_ASSIGNEES,不再区分是发起人选择、还是审批人选择。 Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); hisProcessVariables.putAll(reqVO.getNextAssignees()); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); @@ -554,45 +556,52 @@ public class BpmTaskServiceImpl implements BpmTaskService { /** - * 校验传递的参数中是否为下一个将要执行的任务节点 + * 校验选择的下一个节点的审批人,是否合法 * - * @param taskDefinitionKey 当前任务节点id + * 1. 是否有漏选:没有选择审批人 + * 2. 是否有多选:非下一个节点 + * + * @param taskDefinitionKey 当前任务节点标识 * @param variables 流程变量 * @param bpmnModel 流程模型 - * @param nextActivityNodes 下一个节点审批人集合(参数) + * @param nextAssignees 下一个节点审批人集合(参数) + * @param processInstance 流程实例 */ private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, - Map> nextActivityNodes,ProcessInstance processInstance){ - // 1、获取当前任务节点的信息 + Map> nextAssignees, ProcessInstance processInstance) { + // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - // 2、获取下一个将要执行的节点集合 + // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3、循环下一个将要执行的节点集合 + + // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1、获取下一个将要执行节点的属性(是否为自选审批人等) + // 3.1 获取下一个将要执行节点的属性(是否为自选审批人等) + // TODO @小北:public static Integer parseCandidateStrategy(FlowElement userTask) 使用这个工具方法哈。 Map> extensionElements = nextFlowNode.getExtensionElements(); List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); - if (CollUtil.isEmpty(elements)){ + if (CollUtil.isEmpty(elements)) { continue; } - // 3.2、获取节点中的审批人策略 + // 3.2 获取节点中的审批人策略 Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 3.3、获取流程实例中的发起人自选审批人 + // 3.3 获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.4、如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 + // 3.4 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { // 先判断前端传递的参数节点节点是否为将要执行的节点 - if (!nextActivityNodes.containsKey(nextFlowNode.getId())){ + // TODO @小北:!nextAssignees.containsKey(nextFlowNode.getId())、和 CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) 是不是等价的? + if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果节点存在,则获取节点中的审批人 - List nextAssignees = nextActivityNodes.get(nextFlowNode.getId()); // 如果前端传递的节点为空,则抛出异常 - if (CollUtil.isEmpty(nextAssignees)) { + // TODO @小北:换一个错误码哈。 + if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } + // TODO @小北:加一个“审批人选择”的校验; } } From cb16539b663b2e510f8e67aa9f37b78378c68599 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 1 Mar 2025 17:43:46 +0800 Subject: [PATCH 270/386] =?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 5e15a100cb660aa4c10bc364518a6380173b0dde Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 19:47:16 +0800 Subject: [PATCH 271/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../bpm/service/task/BpmTaskServiceImpl.java | 29 ++++++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 3a50bba523..28335f729b 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,6 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个节点审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); 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 0b5a70ab08..694e126026 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 @@ -530,9 +530,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 - validateNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); // 如果有下一个审批人,则设置到流程变量中 - // TODO @小北:validateNextAssignees 升级成 validateAndSetNextAssignees,然后里面吧下面这一小段逻辑,抽进去如何? if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 // TODO @小北:这里有个需要讨论的点,微信哈; @@ -567,38 +566,28 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param nextAssignees 下一个节点审批人集合(参数) * @param processInstance 流程实例 */ - private void validateNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + private void validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1 获取下一个将要执行节点的属性(是否为自选审批人等) - // TODO @小北:public static Integer parseCandidateStrategy(FlowElement userTask) 使用这个工具方法哈。 - Map> extensionElements = nextFlowNode.getExtensionElements(); - List elements = extensionElements.get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY); - if (CollUtil.isEmpty(elements)) { - continue; - } - // 3.2 获取节点中的审批人策略 - Integer candidateStrategy = Integer.valueOf(elements.get(0).getElementText()); - // 3.3 获取流程实例中的发起人自选审批人 + // 3.1 获取下一个将要执行节点中的审批人策略 + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 3.2 获取流程实例中的发起人自选审批人 Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.4 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 + // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 先判断前端传递的参数节点节点是否为将要执行的节点 - // TODO @小北:!nextAssignees.containsKey(nextFlowNode.getId())、和 CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) 是不是等价的? + // 判断节点是否为执行节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 如果前端传递的节点为空,则抛出异常 - // TODO @小北:换一个错误码哈。 + // 判断节点的审批人是否配置 if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { - throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } } // TODO @小北:加一个“审批人选择”的校验; From 8a3264cfd34b37fe49081c906d50615a1742e5e1 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 20:22:50 +0800 Subject: [PATCH 272/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E6=96=B9=E6=B3=95=E6=8A=BD=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 1eea893f76..743c45f5af 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -808,53 +808,26 @@ public class BpmnModelUtils { // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Gateway gateway = (Gateway) currentElement; - // TODO @小北:当一个网关节点下存在多个满足的并行节点时,只查询一个节点流程流转会存在问题。需要优化,具体见issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/761 - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); - if (matchSequenceFlow == null) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlow = gateway.getOutgoingFlows().get(0); - } - } + SequenceFlow matchSequenceFlow = findMatchSequenceFlow((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); } - return; } - // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 - if (currentElement instanceof InclusiveGateway) { + else if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Gateway gateway = (Gateway) currentElement; - Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); - if (CollUtil.isEmpty(matchSequenceFlows)) { - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlows = gateway.getOutgoingFlows(); - } - } + Collection matchSequenceFlows = findMatchSequenceFlows((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 matchSequenceFlows.forEach( flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); } - // 情况:ParallelGateway 并行,都满足,都走 - if (currentElement instanceof ParallelGateway) { + else if (currentElement instanceof ParallelGateway) { Gateway gateway = (Gateway) currentElement; // 遍历子节点 gateway.getOutgoingFlows().forEach( nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); - return; } } @@ -911,8 +884,25 @@ public class BpmnModelUtils { */ private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { - // TODO @小北: 这里和 simulateNextFlowElements 中有重复代码,是否重构??每个网关节点拆分出方法应该比较合理化,@芋艿 - // TODO @小北:ok,把 simulateNextFlowElements 里面处理网关的,复用这个方法,可以么? + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlow(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理排它网关(Exclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static SequenceFlow findMatchSequenceFlow(Gateway gateway, Map variables){ SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -924,13 +914,7 @@ public class BpmnModelUtils { matchSequenceFlow = gateway.getOutgoingFlows().get(0); } } - // 遍历满足条件的 SequenceFlow 路径 - if (matchSequenceFlow != null) { - FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - } + return matchSequenceFlow; } /** @@ -943,6 +927,26 @@ public class BpmnModelUtils { */ private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径集合 + Collection matchSequenceFlows = findMatchSequenceFlows(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 处理排它网关(Inclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static Collection findMatchSequenceFlows(Gateway gateway, Map variables) { + // 查找满足条件的 SequenceFlow 路径 Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && evalConditionExpress(variables, flow.getConditionExpression())); @@ -954,15 +958,10 @@ public class BpmnModelUtils { matchSequenceFlows = gateway.getOutgoingFlows(); } } - // 遍历满足条件的 SequenceFlow 路径,获取目标节点 - matchSequenceFlows.forEach(flow -> { - FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - }); + return matchSequenceFlows; } + /** * 处理并行网关 * From 3cf0708f64c0110d95eec78a0c56e031c466fb32 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 20:36:01 +0800 Subject: [PATCH 273/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E5=AE=A1?= =?UTF-8?q?=E6=9F=A5=EF=BC=8C=E6=96=B9=E6=B3=95=E6=8A=BD=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 694e126026..be6f33ae08 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 @@ -581,11 +581,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { - // 判断节点是否为执行节点 + // 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 判断节点的审批人是否配置 + // 判断节点的审批人是否配置,节点存在,但未配置审批人 if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } From 9927dd443944d787423726cdfa3f4e00ca7e0bf9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 20:39:23 +0800 Subject: [PATCH 274/386] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91BPM=EF=BC=9A=E5=A2=9E=E5=8A=A0=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=9A=E4=B9=89=E7=9A=84=20simple=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/BpmModelController.java | 2 +- .../definition/BpmProcessDefinitionController.java | 13 +++++++++++++ .../admin/task/BpmProcessInstanceController.java | 12 +++++++++--- .../task/vo/instance/BpmProcessInstanceRespVO.java | 8 ++++++++ .../admin/task/vo/task/BpmTaskPageReqVO.java | 3 +++ .../bpm/convert/task/BpmProcessInstanceConvert.java | 9 +++++++++ .../bpm/service/task/BpmProcessInstanceService.java | 1 - .../module/bpm/service/task/BpmTaskServiceImpl.java | 3 +++ 8 files changed, 46 insertions(+), 5 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java index 6483ee0e39..5578114f9d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java @@ -57,7 +57,7 @@ public class BpmModelController { @GetMapping("/list") @Operation(summary = "获得模型分页") @Parameter(name = "name", description = "模型名称", example = "芋艿") - public CommonResult> getModelPage(@RequestParam(value = "name", required = false) String name) { + public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { List list = modelService.getModelList(name); if (CollUtil.isEmpty(list)) { return success(Collections.emptyList()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java index 28cbd0ab98..90c38886a7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java @@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.ProcessDefinition; import org.springframework.security.access.prepost.PreAuthorize; @@ -31,6 +32,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.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @@ -99,6 +101,17 @@ public class BpmProcessDefinitionController { list, null, processDefinitionMap, null, null)); } + @GetMapping("/simple-list") + @Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项") + public CommonResult> getSimpleProcessDefinitionList() { + // 只查询未挂起的流程 + List list = processDefinitionService.getProcessDefinitionListBySuspensionState( + SuspensionState.ACTIVE.getStateCode()); + // 拼接 VO 返回,只返回 id、name、key + return success(convertList(list, definition -> new BpmProcessDefinitionRespVO() + .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey()))); + } + @GetMapping ("/get") @Operation(summary = "获得流程定义") @Parameter(name = "id", description = "流程编号", required = true, example = "1024") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index def95cca63..7ce43a32c2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -32,10 +32,10 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; +import java.util.Set; 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.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” @@ -78,8 +78,14 @@ public class BpmProcessInstanceController { convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Set userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())); + userIds.addAll(convertSetByFlatMap(taskMap.values(), + tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap( + convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, - processDefinitionMap, categoryMap, taskMap, null, null, processDefinitionInfoMap)); + processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); } @GetMapping("/manager-page") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java index 76dca606a2..f73c490a54 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -73,6 +74,13 @@ public class BpmProcessInstanceRespVO { @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + private UserSimpleBaseVO assigneeUser; + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java index 11c59ce3ed..f129e5a315 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -18,6 +18,9 @@ public class BpmTaskPageReqVO extends PageParam { @Schema(description = "流程分类", example = "1") private String category; + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + @Schema(description = "创建时间") @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java index 6e798e77f2..ccd7f06e74 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -76,6 +76,15 @@ public interface BpmProcessInstanceConvert { respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName())); } + if (CollUtil.isNotEmpty(respVO.getTasks())) { + respVO.getTasks().forEach(task -> { + AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee()); + if (assigneeUser!= null) { + task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class)); + MapUtils.findAndThen(deptMap, assigneeUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName())); + } + }); + } } // 摘要 respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index c4684d3402..6b9c6fa3e7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -85,7 +85,6 @@ public interface BpmProcessInstanceService { PageResult getProcessInstancePage(Long userId, @Valid BpmProcessInstancePageReqVO pageReqVO); - // TODO @芋艿:重点在 review 下 /** * 获取审批详情。 *

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 0b5a70ab08..5d9f1552b4 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 @@ -122,6 +122,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (StrUtil.isNotEmpty(pageVO.getCategory())) { taskQuery.taskCategory(pageVO.getCategory()); } + if (StrUtil.isNotEmpty(pageVO.getProcessDefinitionKey())) { + taskQuery.processDefinitionKey(pageVO.getProcessDefinitionKey()); + } if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); From 4d35a272c340b132c7dfc283d3fa01bf16f90db9 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sat, 1 Mar 2025 22:30:40 +0800 Subject: [PATCH 275/386] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=B8=B8=E9=87=8FPROCESS=5FINSTANCE=5FVARIABLE=5FAPPROVE=5FUSE?= =?UTF-8?q?R=5FSELECT=5FASSIGNEES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/enums/BpmnVariableConstants.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 119a441857..c10d91dcf2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -29,6 +29,12 @@ public class BpmnVariableConstants { * @see ProcessInstance#getProcessVariables() */ public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 审批人选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; /** * 流程实例的变量 - 发起用户 ID * From 53693529e1d06a417f7e1d8f9f68e0478cdb4fca Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 1 Mar 2025 23:30:47 +0800 Subject: [PATCH 276/386] =?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 277/386] =?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 d8a7d668a4658b9ece574e4eb497795889bd6a04 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Mar 2025 09:56:54 +0800 Subject: [PATCH 278/386] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E3=80=91BPM=EF=BC=9A=E6=94=AF=E6=8C=81=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E2=80=9C=E5=8E=86=E5=8F=B2=E2=80=9D=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=81=A2=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/vo/model/BpmModelRespVO.java | 2 +- .../process/BpmProcessDefinitionRespVO.java | 29 ++++--------------- .../BpmProcessDefinitionInfoDO.java | 11 +++++-- .../BpmProcessDefinitionServiceImpl.java | 3 +- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index 278291483d..275368c7c6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -25,7 +25,7 @@ public class BpmModelRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") private String icon; - @Schema(description = "流程分类编码", example = "1") + @Schema(description = "流程分类编号", example = "1") private String category; @Schema(description = "流程分类名字", example = "请假") private String categoryName; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 1e9dfc8207..8da290440e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -1,14 +1,14 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -import java.util.List; @Schema(description = "管理后台 - 流程定义 Response VO") @Data -public class BpmProcessDefinitionRespVO { +public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; @@ -22,36 +22,19 @@ public class BpmProcessDefinitionRespVO { @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") private String key; - @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") - private String icon; - - @Schema(description = "流程描述", example = "我是描述") - private String description; - @Schema(description = "流程分类", example = "1") private String category; @Schema(description = "流程分类名字", example = "请假") private String categoryName; + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String modelId; + @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 - @Schema(description = "表单类型-参见 bpm_model_form_type 数据字典", example = "1") - private Integer formType; - @Schema(description = "表单编号-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", example = "1024") - private Long formId; @Schema(description = "表单名字", example = "请假表单") private String formName; - @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private String formConf; - @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private List formFields; - @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", - example = "/bpm/oa/leave/create") - private String formCustomCreatePath; - @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址-在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", - example = "/bpm/oa/leave/view") - private String formCustomViewPath; @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 @@ -67,7 +50,7 @@ public class BpmProcessDefinitionRespVO { @Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long sort; - + @Schema(description = "BPMN UserTask 用户任务") @Data public static class UserTask { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index ccab7b0435..34dfbece3a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.definition; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler; -import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; @@ -60,6 +59,14 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { */ private Integer modelType; + /** + * 流程分类的编码 + * + * 关联 {@link BpmCategoryDO#getCode()} + * + * 为什么要存储?原因是,{@link ProcessDefinition#getCategory()} 无法设置 + */ + private String category; /** * 图标 */ @@ -149,7 +156,7 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { * * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 */ - @TableField(typeHandler = StringListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 + @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 private List managerUserIds; /** diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java index 96280fa088..14ee084063 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -143,9 +143,8 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ // 插入拓展表 BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) - .setModelId(model.getId()).setProcessDefinitionId(definition.getId()) + .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId()) .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson); - if (form != null) { definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); } 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 279/386] =?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 9dfed5365b2b3216406e53603ffd9cab397003c2 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 21:06:28 +0800 Subject: [PATCH 280/386] =?UTF-8?q?feat=EF=BC=9A=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=EF=BC=9A=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89=E6=A0=87?= =?UTF-8?q?=E8=AF=86=EF=BC=8C=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89=E7=AD=96=E7=95=A5=EF=BC=8C?= =?UTF-8?q?=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E8=8E=B7=E5=BE=97=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=9E=E4=BE=8B=E7=9A=84=E5=AE=A1=E6=89=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=80=89=E6=8B=A9=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E9=94=99=E8=AF=AF=E7=A0=811=5F009?= =?UTF-8?q?=5F004=5F007=EF=BC=8C=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA=E6=9C=AA=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 2 +- ...askCandidateApproveUserSelectStrategy.java | 73 +++++++++++++++++++ .../enums/BpmTaskCandidateStrategyEnum.java | 1 + .../flowable/core/util/FlowableUtils.java | 25 +++++++ .../bpm/service/task/BpmTaskServiceImpl.java | 49 +++++++------ 5 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 28335f729b..7fb85b7835 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个节点审批人未配置"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个执行节点审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java new file mode 100644 index 0000000000..c910f08b18 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.google.common.collect.Sets; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * 审批人在审批时选择下一个节点的审批人 + * @author smallNorthLee + */ +@Component +public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", + execution.getProcessInstanceId()); + // 获得审批人 + List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 990bc6303a..0cdb56fb90 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,6 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), + APPROVE_USER_SELECT(34, "审批人自选"), // 审批人在审批时选择下一个节点的审批人 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 6022b8fe93..fe83495e35 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -200,6 +200,31 @@ public class FlowableUtils { BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); } + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processInstance 流程实例 + * @return 审批用户选择的下一个节点的审批人Map + */ + public static Map> getApproveUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processVariables 流程变量 + * @return 审批用户选择的下一个节点的审批人Map Map + */ + @SuppressWarnings("unchecked") + public static Map> getApproveUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return null; + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + } + /** * 获得流程实例的摘要 * 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 be6f33ae08..4b82ff991b 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 @@ -528,22 +528,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { - Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables()); // 校验传递的参数中是否为下一个将要执行的任务节点 - validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 如果有下一个审批人,则设置到流程变量中 - if (CollUtil.isNotEmpty(reqVO.getNextAssignees())) { - // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - // TODO @小北:这里有个需要讨论的点,微信哈; - // TODO 因为 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES 定位是发起人,那么审批人选择的,放在 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES。目前想到两个方案: - // TODO 方案一:增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,然后设置到这里面。然后,BpmTaskCandidateStartUserSelectStrategy 也从这里读 - // TODO 方案二:也是增加一个 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES,根据节点审批人类型,放到 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES、PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES - // TODO 方案三:融合成 PROCESS_INSTANCE_VARIABLE_USER_SELECT_ASSIGNEES,不再区分是发起人选择、还是审批人选择。 - Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(instance.getProcessVariables()); - hisProcessVariables.putAll(reqVO.getNextAssignees()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - } - runtimeService.setVariables(task.getProcessInstanceId(), variables); + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + bpmnModel, reqVO.getNextAssignees(), instance); taskService.complete(task.getId(), variables, true); } else { taskService.complete(task.getId()); @@ -566,21 +553,29 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param nextAssignees 下一个节点审批人集合(参数) * @param processInstance 流程实例 */ - private void validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + // 获取流程实例中的审批人自选审批人 + Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isEmpty(hisProcessVariables)){ + hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + } + // 校验通过的全部节点和审批人 + Map> allNextAssignees = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 获取流程实例中的发起人自选审批人 - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - List startUserSelectAssignee = startUserSelectAssignees.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) && CollUtil.isEmpty(startUserSelectAssignee)) { + // 3.2 获取流程实例中的审批人自选审批人 + List approveUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + // 3.3 如果节点中的审批人策略为 发起人自选或者审批人自选,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) + || ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy()) + && CollUtil.isEmpty(approveUserSelectAssignee)) { // 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); @@ -589,9 +584,21 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + // 4.3.3 校验通过的全部节点和审批人 + allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); } // TODO @小北:加一个“审批人选择”的校验; } + // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 + Map newVariables = FlowableUtils.filterTaskFormVariable(variables); + // 4. 如果下个节点审批参数不为空,则设置流程变量 + if (CollUtil.isNotEmpty(allNextAssignees)) { + // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 + hisProcessVariables.putAll(allNextAssignees); + newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); + runtimeService.setVariables(processInstance.getProcessInstanceId(), variables); + } + return newVariables; } /** From 1b08e6828fd08714ed9c3dfe46b54b177b55d647 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 2 Mar 2025 22:31:06 +0800 Subject: [PATCH 281/386] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 11 +++++ .../task/BpmProcessInstanceService.java | 10 +++++ .../task/BpmProcessInstanceServiceImpl.java | 45 +++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index def95cca63..21d695ac49 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -178,4 +178,15 @@ public class BpmProcessInstanceController { public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } + + @GetMapping("/get-next-flow-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextFlowNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextFlowNodes(getLoginUserId(), reqVO)); + } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index c4684d3402..260a9fbd42 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -97,6 +97,15 @@ public interface BpmProcessInstanceService { */ BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + /** + * 获取下一个执行节点信息。 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 下一个执行节点信息 + */ + List getNextFlowNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + /** * 获取流程实例的 BPMN 模型视图 * @@ -173,4 +182,5 @@ public interface BpmProcessInstanceService { * @param instance 流程任务 */ void processProcessInstanceCompleted(ProcessInstance instance); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 9da2610cb6..604a228a1c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -11,6 +11,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +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.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -55,6 +56,7 @@ import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstanceBuilder; +import org.flowable.task.api.Task; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -225,6 +227,49 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask); } + @Override + public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1 校验任务存在 + Task task = taskService.getTask(reqVO.getTaskId()); + if (task == null) { + throw exception(TASK_NOT_EXISTS); + } + // 2 校验任务是否由当前用户审批 + if (StrUtil.isNotBlank(task.getAssignee()) + && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + // 3 校验流程实例存在 + ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + // 4 获取 BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + // 5. 获取当前任务节点的信息 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, reqVO.getProcessVariables()); + return convertList(nextFlowNodes, nodes -> { + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, nodes.getId()); + ActivityNode activityNode = new ActivityNode().setId(nodes.getId()).setName(nodes.getName()) + .setNodeType(START_USER_NODE_ID.equals(nodes.getId()) + ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(FlowableUtils.getTaskStatus(task)) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)); + + // 如果是取消状态,则跳过 + if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { + return null; + } + return activityNode; + }); + } + @Override @SuppressWarnings("unchecked") public PageResult getProcessInstancePage(Long userId, From 61ea09488ed624d1f15fc01b8ffa98aa717484b7 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 3 Mar 2025 12:22:19 +0800 Subject: [PATCH 282/386] =?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 283/386] =?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 284/386] =?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 33ab9616291bd49cbbf82d5fde5c0ff184d05baf Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Mon, 3 Mar 2025 17:36:13 +0800 Subject: [PATCH 285/386] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E8=8A=82=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 61 +++++++++++++------ 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 604a228a1c..fd93110723 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -15,6 +15,7 @@ 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.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; @@ -249,24 +250,43 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (bpmnModel == null) { return null; } - // 5. 获取当前任务节点的信息 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + Map processVariables = new HashMap<>(); + // 获取历史中流程变量 + Map historicVariables = historicProcessInstance.getProcessVariables(); + if (CollUtil.isNotEmpty(historicVariables)) { + processVariables.putAll(historicVariables); + } + // 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())){ + processVariables.putAll(reqVO.getProcessVariables()); + } + // 获取流程定义信息 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(task.getProcessDefinitionId()); + // 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, reqVO.getProcessVariables()); - return convertList(nextFlowNodes, nodes -> { - FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, nodes.getId()); - ActivityNode activityNode = new ActivityNode().setId(nodes.getId()).setName(nodes.getName()) - .setNodeType(START_USER_NODE_ID.equals(nodes.getId()) - ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) - .setStatus(FlowableUtils.getTaskStatus(task)) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)); - - // 如果是取消状态,则跳过 - if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { - return null; + // 获取下一个将要执行的节点集合 + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); + return convertList(nextFlowNodes, node -> { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + loginUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + //存在一个节点多人审批的情况 + List candidateUsers = new ArrayList<>(); + for (Long candidateUserId : candidateUserIds) { + Map userMap = adminUserApi.getUserMap(candidateUserIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(candidateUserId, userMap, deptMap)); } - return activityNode; + return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setId(node.getId()) + .setName(node.getName()) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUsers(candidateUsers); }); } @@ -725,8 +745,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // true,不影响没配置 // skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { + // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); + // 设置流程变量,审批人自选审批人 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, + startUserSelectAssignees); } // 3. 创建流程 @@ -768,9 +792,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } - // 2.1 移除掉不是发起人自选审批人节点 + // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) + && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From d41cce94cd1d6c7cc515b3c1023676d3584a3244 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 21:24:13 +0800 Subject: [PATCH 286/386] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=B5=81=E7=A8=8B=E6=89=A7=E8=A1=8C=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E4=B8=8E=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/BpmnModelUtils.java | 4 ++++ .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 743c45f5af..c3d033c05c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -856,6 +856,10 @@ public class BpmnModelUtils { if (targetElement == null) { continue; } + // 如果是结束节点,直接返回 + if (targetElement instanceof EndEvent) { + break; + } // 情况一:处理不同类型的网关 if (targetElement instanceof Gateway) { Gateway gateway = (Gateway) targetElement; 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 4b82ff991b..ee37dc4d70 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 @@ -595,8 +595,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (CollUtil.isNotEmpty(allNextAssignees)) { // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 hisProcessVariables.putAll(allNextAssignees); + // 设置流程变量,审批人自选 newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); - runtimeService.setVariables(processInstance.getProcessInstanceId(), variables); + // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 + newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + runtimeService.setVariables(processInstance.getProcessInstanceId(), newVariables); } return newVariables; } From 7ab61b6d061801c5a24cd1d974ad382f174da1fe Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 3 Mar 2025 21:50:52 +0800 Subject: [PATCH 287/386] =?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 6e2d00d561d5b7b866f90012da43d83eb8c7c911 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Mon, 3 Mar 2025 22:38:05 +0800 Subject: [PATCH 288/386] =?UTF-8?q?feat=EF=BC=9A=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E6=97=B6=EF=BC=8C=E6=9F=A5=E8=AF=A2=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E8=8A=82=E7=82=B9=EF=BC=8C?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=B5=81=E7=A8=8B=E6=89=A7=E8=A1=8C=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E4=B8=8E=E5=90=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/task/BpmProcessInstanceServiceImpl.java | 10 ++++++---- .../module/bpm/service/task/BpmTaskServiceImpl.java | 7 +++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fd93110723..8dde5b208d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -169,7 +169,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Long startUserId = loginUserId; // 流程发起人 HistoricProcessInstance historicProcessInstance = null; // 流程实例 Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 - Map processVariables = reqVO.getProcessVariables(); // 流程变量 + Map processVariables = new HashMap<>(); // 流程变量 // 1.2 如果是流程已发起的场景,则使用流程实例的数据 if (reqVO.getProcessInstanceId() != null) { historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); @@ -180,10 +180,12 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); // 合并 DB 和前端传递的流量变量,以前端的为主 Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(processVariables)) { - historicVariables.putAll(processVariables); + if (CollUtil.isNotEmpty(historicVariables)) { + processVariables.putAll(historicVariables); } - processVariables = historicVariables; + } + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); } // 1.3 读取其它相关数据 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( 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 ee37dc4d70..2a89be970e 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 @@ -531,6 +531,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 校验传递的参数中是否为下一个将要执行的任务节点 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); } else { taskService.complete(task.getId()); @@ -555,6 +556,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { + // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 + if (CollUtil.isEmpty(nextAssignees)){ + return variables; + } // 1. 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 @@ -587,7 +592,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 4.3.3 校验通过的全部节点和审批人 allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); } - // TODO @小北:加一个“审批人选择”的校验; } // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 Map newVariables = FlowableUtils.filterTaskFormVariable(variables); @@ -599,7 +603,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - runtimeService.setVariables(processInstance.getProcessInstanceId(), newVariables); } return newVariables; } From 71b45a29a304e033ea29dafc546873caa50c6678 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 4 Mar 2025 20:13:19 +0800 Subject: [PATCH 289/386] =?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 dadd43677eb90d5df6929030b960b59174c946f6 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:07:58 +0800 Subject: [PATCH 290/386] =?UTF-8?q?review:=2051=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateApproveUserSelectStrategy.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index c910f08b18..27c08c0ada 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -50,6 +50,9 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", execution.getProcessInstanceId()); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } // 获得审批人 List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); @@ -62,6 +65,8 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa return Sets.newLinkedHashSet(); } Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + Assert.notNull(approveUserSelectAssignees, "流程实例节点({}) 的下一个执行节点审批人不能为空", + activityId); if (approveUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } From 4e57bd157fa61cbcb8af1f2f9ca350ea84de6dd6 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:09:11 +0800 Subject: [PATCH 291/386] =?UTF-8?q?review:=2027=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/enums/BpmTaskCandidateStrategyEnum.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/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 0cdb56fb90..9b8474ebf0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,7 +24,7 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), - APPROVE_USER_SELECT(34, "审批人自选"), // 审批人在审批时选择下一个节点的审批人 + APPROVE_USER_SELECT(34, "审批人,在审批时选择下一个节点的审批人"), // 审批人,在审批时选择下一个节点的审批人 START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), From 9e151b396676f82f5cd8e8ba07998a6d60f44b7c Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:28:25 +0800 Subject: [PATCH 292/386] =?UTF-8?q?review:=20278=20281=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 115 +++++++++--------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 8dde5b208d..9111176fd4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -149,7 +149,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) @@ -232,57 +232,62 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Override public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1 校验任务存在 + // 1.1 校验任务存在 Task task = taskService.getTask(reqVO.getTaskId()); if (task == null) { throw exception(TASK_NOT_EXISTS); } - // 2 校验任务是否由当前用户审批 + // 1.2 校验任务是否由当前用户审批 if (StrUtil.isNotBlank(task.getAssignee()) && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); } - // 3 校验流程实例存在 + // 1.3 校验流程实例存在 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); if (instance == null) { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } - // 4 获取 BpmnModel + // 1.4 校验BpmnModel BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); if (bpmnModel == null) { return null; } + //1.5 流程实例是否存在 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); if (historicProcessInstance == null) { throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); } + + // 2 设置流程变量 Map processVariables = new HashMap<>(); - // 获取历史中流程变量 + // 2.1 获取历史中流程变量 Map historicVariables = historicProcessInstance.getProcessVariables(); if (CollUtil.isNotEmpty(historicVariables)) { processVariables.putAll(historicVariables); } - // 合并前端传递的流程变量,以前端为准 - if (CollUtil.isNotEmpty(reqVO.getProcessVariables())){ + // 2.2 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { processVariables.putAll(reqVO.getProcessVariables()); } - // 获取流程定义信息 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService - .getProcessDefinitionInfo(task.getProcessDefinitionId()); - // 获取当前任务节点的信息 + // 3 获取当前任务节点的信息 FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - // 获取下一个将要执行的节点集合 + // 3.1 获取下一个将要执行的节点集合 List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); return convertList(nextFlowNodes, node -> { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), - loginUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); - //存在一个节点多人审批的情况 - List candidateUsers = new ArrayList<>(); - for (Long candidateUserId : candidateUserIds) { - Map userMap = adminUserApi.getUserMap(candidateUserIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); - candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(candidateUserId, userMap, deptMap)); + loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); + // 3.2 获取节点的审批人信息 + Map userMap = adminUserApi.getUserMap(candidateUserIds); + if (CollUtil.isEmpty(userMap)) { + return null; } + // 3.3 获取节点的审批人部门信息 + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); + // 3.4 存在一个节点多人审批的情况,组装审批人信息 + List candidateUsers = new ArrayList<>(); + userMap.forEach((key, value) -> { + candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap)); + }); return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setId(node.getId()) .setName(node.getName()) @@ -356,15 +361,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -386,9 +391,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -400,7 +405,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) .setStatus(FlowableUtils.getTaskStatus(task)) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) @@ -456,11 +461,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -521,9 +526,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -545,8 +550,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -590,8 +595,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } @@ -622,7 +627,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private List getTaskCandidateUserList(BpmnModel bpmnModel, String activityId, - Long startUserId, String processDefinitionId, Map processVariables) { + Long startUserId, String processDefinitionId, Map processVariables) { Set userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, startUserId, processDefinitionId, processVariables); return new ArrayList<>(userIds); @@ -658,11 +663,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Set finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, activityInstance -> activityInstance.getEndTime() != null && ObjectUtil.notEqual(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); Set finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, activityInstance -> activityInstance.getEndTime() != null && ObjectUtil.equals(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉 finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds); // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。 @@ -714,8 +719,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private String createProcessInstance0(Long userId, ProcessDefinition definition, - Map variables, String businessKey, - Map> startUserSelectAssignees) { + Map variables, String businessKey, + Map> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); @@ -744,8 +749,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 - // true,不影响没配置 - // skipExpression 的节点 + // true,不影响没配置 + // skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, @@ -784,20 +789,20 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, Map> startUserSelectAssignees, - Map variables) { + Map variables) { // 1. 获取预测的节点信息 BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() .setProcessDefinitionId(definition.getId()) .setProcessVariables(variables)); List activityNodes = detailRespVO.getActivityNodes(); - if (CollUtil.isEmpty(activityNodes)){ + if (CollUtil.isEmpty(activityNodes)) { return; } // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) - && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From c16f7b8154d0691e2bb5846564a07e17b76e5534 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:54:44 +0800 Subject: [PATCH 293/386] =?UTF-8?q?review:=2024=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java | 1 + 1 file changed, 1 insertion(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index 27c08c0ada..fd5e121196 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -21,6 +21,7 @@ import java.util.Map; /** * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 * 审批人在审批时选择下一个节点的审批人 + * * @author smallNorthLee */ @Component From ebd722cb4162da1c2160661ead9589cda508d55b Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 22:59:22 +0800 Subject: [PATCH 294/386] =?UTF-8?q?review:=20=E4=BF=AE=E6=94=B9=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E4=B8=8B=E4=B8=80=E4=B8=AA=E6=89=A7=E8=A1=8C=E7=9A=84?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=8E=A5=E5=8F=A3=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E5=92=8C=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 21 ++++++++++--------- .../task/BpmProcessInstanceService.java | 2 +- .../task/BpmProcessInstanceServiceImpl.java | 11 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 21d695ac49..a7dddf2645 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -172,6 +172,17 @@ public class BpmProcessInstanceController { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } + @GetMapping("/get-next-approval-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO)); + } + @GetMapping("/get-bpmn-model-view") @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") @Parameter(name = "id", description = "流程实例的编号", required = true) @@ -179,14 +190,4 @@ public class BpmProcessInstanceController { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } - @GetMapping("/get-next-flow-nodes") - @Operation(summary = "获取下一个执行的流程节点") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - @SuppressWarnings("unchecked") - public CommonResult> getNextFlowNodes(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); - } - return success(processInstanceService.getNextFlowNodes(getLoginUserId(), reqVO)); - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 260a9fbd42..69f64c4dbc 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -104,7 +104,7 @@ public interface BpmProcessInstanceService { * @param reqVO 请求信息 * @return 下一个执行节点信息 */ - List getNextFlowNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + List getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); /** * 获取流程实例的 BPMN 模型视图 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 9111176fd4..fc61cc038a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -231,7 +231,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } @Override - public List getNextFlowNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { // 1.1 校验任务存在 Task task = taskService.getTask(reqVO.getTaskId()); if (task == null) { @@ -755,9 +755,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); - // 设置流程变量,审批人自选审批人 - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, - startUserSelectAssignees); +// // 设置流程变量,审批人自选审批人 +// variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, +// startUserSelectAssignees); } // 3. 创建流程 @@ -801,8 +801,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 2.1 移除掉不是发起人或者审批人自选审批人节点 activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy()) - && ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 activityNodes.forEach(task -> { List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; From ffa7c246cfafacc53c326b0d86ded26712402943 Mon Sep 17 00:00:00 2001 From: jason <2667446@qq.com> Date: Tue, 4 Mar 2025 23:17:35 +0800 Subject: [PATCH 295/386] =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=20=E5=90=8C=E4=B8=80=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=85=88=E6=9F=A5=E8=AF=A2=E4=BC=A0=E9=80=92=E7=9A=84?= =?UTF-8?q?=20taskId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 4 +- .../bpm/service/task/BpmTaskService.java | 7 +- .../bpm/service/task/BpmTaskServiceImpl.java | 67 ++++++++++++------- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 0bb8a29d80..640ac85dc8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -207,9 +207,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } // 3.1 计算当前登录用户的待办任务 - // TODO @jason:有一个极端情况,如果一个用户有 2 个 task A 和 B,A 已经通过,B 需要审核。这个时,通过 A 进来,todo 拿到 - // B,会不会表单权限不一致哈。 - BpmTaskRespVO todoTask = taskService.getFirstTodoTask(loginUserId, reqVO.getProcessInstanceId()); + BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); // 3.2 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index a40fadba75..ff8dcca9fe 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -35,13 +35,16 @@ public interface BpmTaskService { PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); /** - * 获得用户在指定流程下,首个需要处理(待办)的任务 + * 获得用户(待办)的任务。 + * 1、根据 id 查询待办任务 + * 2、如果任务不存在,获取指定流程下,首个需要处理任务 * * @param userId 用户编号 + * @param id 任务编号 * @param processInstanceId 流程实例编号 * @return 待办任务 */ - BpmTaskRespVO getFirstTodoTask(Long userId, String processInstanceId); + BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId); /** * 获得已办的流程任务分页 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 483741868e..ff6eb46186 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 @@ -133,32 +133,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override - public BpmTaskRespVO getFirstTodoTask(Long userId, String processInstanceId) { - if (processInstanceId == null) { - return null; + public BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId) { + // 1.1 获取指定的用户待办任务 + Task todoTask = getMyTodoTask(userId, id); + if (todoTask == null) { + // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 + todoTask = getFirstMyTodoTask(userId, processInstanceId); } - // 1. 查询所有任务 - List tasks = taskService.createTaskQuery() - .active() - .processInstanceId(processInstanceId) - .includeTaskLocalVariables() - .includeProcessVariables() - .orderByTaskCreateTime().asc() // 按创建时间升序 - .list(); - if (CollUtil.isEmpty(tasks)) { - return null; - } - - // 2.1 查询我的首个任务 - Task todoTask = CollUtil.findOne(tasks, task -> { - return isAssignUserTask(userId, task) // 当前用户为审批人 - || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) - }); if (todoTask == null) { return null; } - // 2.2 查询该任务的子任务 - List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), tasks); + + // 2.查询该任务的子任务 + List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); // 3. 转换返回 BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId()); @@ -178,6 +165,40 @@ public class BpmTaskServiceImpl implements BpmTaskService { .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); } + private Task getMyTodoTask(Long userId, String id) { + if (StrUtil.isEmpty(id)) { + return null; + } + Task task = getTask(id); + if (task == null) { + return null; + } + if (!isAssignUserTask(userId, task) && !isAddSignUserTask(userId, task)) { + return null; + } + return task; + } + + private Task getFirstMyTodoTask(Long userId, String processInstanceId) { + if (processInstanceId == null) { + return null; + } + // 1. 查询所有任务 + List tasks = taskService.createTaskQuery() + .active() + .processInstanceId(processInstanceId) + .includeTaskLocalVariables() + .includeProcessVariables() + .orderByTaskCreateTime().asc() // 按创建时间升序 + .list(); + + // 2. 查询我的首个任务 + return CollUtil.findOne(tasks, task -> { + return isAssignUserTask(userId, task) // 当前用户为审批人 + || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) + }); + } + @Override public PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) { HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() @@ -1230,7 +1251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); if (userTaskElement.getId().equals(START_USER_NODE_ID) && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 - || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 + || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); From 1e3fd24d651659cf58fcaa613fb74ea2b93674fc Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 4 Mar 2025 23:18:19 +0800 Subject: [PATCH 296/386] =?UTF-8?q?review:=20=E6=B5=81=E7=A8=8B=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E6=97=B6=EF=BC=8C=E5=8F=AA=E9=A2=84=E6=B5=8B=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=BA=E5=8F=91=E8=B5=B7=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=E7=9A=84=E8=8A=82=E7=82=B9=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dept/BpmTaskCandidateApproveUserSelectStrategy.java | 3 +-- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java index fd5e121196..a316925655 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -65,9 +65,8 @@ public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCa if (processVariables == null) { return Sets.newLinkedHashSet(); } + // 流程预测时会使用,允许审批人为空,如果为空前端会弹出提示选择下一个节点审批人,避免流程无法进行,审批时会真正校验节点是否配置审批人 Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); - Assert.notNull(approveUserSelectAssignees, "流程实例节点({}) 的下一个执行节点审批人不能为空", - activityId); if (approveUserSelectAssignees == null) { return Sets.newLinkedHashSet(); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fc61cc038a..080529f778 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -224,6 +224,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); + // 3.3 如果时发起动作,activityId为开始节点,不校验审批人自选节点 + if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ + simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + } // 4. 拼接最终数据 return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, From 1f9769a4329ad361b73ab6cb33595b01e29f0bd0 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 00:36:48 +0800 Subject: [PATCH 297/386] =?UTF-8?q?review:=20=E6=8B=86=E5=88=86=E5=8F=91?= =?UTF-8?q?=E8=B5=B7=E4=BA=BA=E8=87=AA=E9=80=89=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=20=E5=92=8C=20=E5=AE=A1=E6=89=B9=E4=BA=BA=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 3 - .../bpm/service/task/BpmTaskServiceImpl.java | 63 ++++++++++--------- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 080529f778..ba72e25b23 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -282,9 +282,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); // 3.2 获取节点的审批人信息 Map userMap = adminUserApi.getUserMap(candidateUserIds); - if (CollUtil.isEmpty(userMap)) { - return null; - } // 3.3 获取节点的审批人部门信息 Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 3.4 存在一个节点多人审批的情况,组装审批人信息 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 2a89be970e..30d0e9c5b1 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 @@ -564,47 +564,48 @@ public class BpmTaskServiceImpl implements BpmTaskService { FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 获取流程实例中的审批人自选审批人 - Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isEmpty(hisProcessVariables)){ - hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - } - // 校验通过的全部节点和审批人 - Map> allNextAssignees = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 获取流程实例中的审批人自选审批人 - List approveUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选或者审批人自选,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()) - || ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy()) - && CollUtil.isEmpty(approveUserSelectAssignee)) { - // 判断节点是否为执行节点,仅校验节点 - if (!nextAssignees.containsKey(nextFlowNode.getId())) { - throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + // 3.2 判断节点是否为执行节点,仅校验节点 + if (!nextAssignees.containsKey(nextFlowNode.getId())) { + throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); + } + // 3.3 获取节点中的审批人 + List assignees = nextAssignees.get(nextFlowNode.getId()); + // 3.3 如果节点中的审批人策略为 发起人自选 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { + Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + List startUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + // 如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(startUserSelectAssignee)) { + continue; } // 判断节点的审批人是否配置,节点存在,但未配置审批人 - if (CollUtil.isEmpty(nextAssignees.get(nextFlowNode.getId()))) { + if (CollUtil.isEmpty(assignees)){ + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + // 校验通过的全部节点和审批人 + hisProcessVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + } + // 3.4 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + // 判断节点的审批人是否配置,节点存在,但未配置审批人 + if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } - // 4.3.3 校验通过的全部节点和审批人 - allNextAssignees.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (hisProcessVariables == null) { + hisProcessVariables = new HashMap<>(); + // 校验通过的全部节点和审批人 + hisProcessVariables.put(nextFlowNode.getId(), assignees); + } + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); } } - // variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - Map newVariables = FlowableUtils.filterTaskFormVariable(variables); - // 4. 如果下个节点审批参数不为空,则设置流程变量 - if (CollUtil.isNotEmpty(allNextAssignees)) { - // 获取实例中的全部节点数据,避免后续节点的审批人被覆盖 - hisProcessVariables.putAll(allNextAssignees); - // 设置流程变量,审批人自选 - newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); - // 设置流程变量,发起人自选,后续的节点或者回退后再或者驳回重新发起的场景可能存在发起人自选或者审批人自选,所以中两个变量值要保持一致,否则会查询不到审批人 - newVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); - } - return newVariables; + return variables; } /** From 1e2b56256c5c633566d9c273388ccada080ab6df Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 09:25:00 +0800 Subject: [PATCH 298/386] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index ba72e25b23..0aa7dcea57 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -286,9 +286,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); // 3.4 存在一个节点多人审批的情况,组装审批人信息 List candidateUsers = new ArrayList<>(); - userMap.forEach((key, value) -> { - candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap)); - }); + userMap.forEach((key, value) -> candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap))); return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) .setId(node.getId()) .setName(node.getName()) From 672a5ef538c2374a142e919ebd40ed3fcb4a2eaf Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:30:38 +0800 Subject: [PATCH 299/386] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskServiceImpl.java | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) 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 30d0e9c5b1..1b17e53351 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 @@ -564,6 +564,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + Map> processVariables = new HashMap<>(); // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { // 3.1 获取下一个将要执行节点中的审批人策略 @@ -574,35 +575,35 @@ public class BpmTaskServiceImpl implements BpmTaskService { } // 3.3 获取节点中的审批人 List assignees = nextAssignees.get(nextFlowNode.getId()); - // 3.3 如果节点中的审批人策略为 发起人自选 + // 3.4 流程变量 + // 3.5 如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { - Map> hisProcessVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - List startUserSelectAssignee = hisProcessVariables.get(nextFlowNode.getId()); + processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if(processVariables == null){ + processVariables = new HashMap<>(); + } + List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { continue; } - // 判断节点的审批人是否配置,节点存在,但未配置审批人 + // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)){ throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 - hisProcessVariables.put(nextFlowNode.getId(), assignees); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, hisProcessVariables); + processVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } - // 3.4 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + // 3.6 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ - // 判断节点的审批人是否配置,节点存在,但未配置审批人 + // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } - Map> hisProcessVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (hisProcessVariables == null) { - hisProcessVariables = new HashMap<>(); - // 校验通过的全部节点和审批人 - hisProcessVariables.put(nextFlowNode.getId(), assignees); - } - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, hisProcessVariables); + // 校验通过的全部节点和审批人 + processVariables.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } return variables; From ead8e94deb554e19bf99a53237afff23b20ab39f Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:37:32 +0800 Subject: [PATCH 300/386] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceServiceImpl.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 0aa7dcea57..e09c6452a1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -224,7 +224,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); - // 3.3 如果时发起动作,activityId为开始节点,不校验审批人自选节点 + // 3.3 如果是发起动作,activityId为开始节点,不校验审批人自选节点 if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } @@ -360,15 +360,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -390,9 +390,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -460,11 +460,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -525,9 +525,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -549,8 +549,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -594,8 +594,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } From a23ce60041a32859e6df916fa9f4d1da03bb322a Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Wed, 5 Mar 2025 12:39:43 +0800 Subject: [PATCH 301/386] =?UTF-8?q?review=EF=BC=9A=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.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/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index e09c6452a1..0309ecd8a4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -149,7 +149,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) From 847e51269b8934823963d7e5822353de3c634c4a Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 21:40:44 +0800 Subject: [PATCH 302/386] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=20=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9E=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=98=AF=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 1 + .../definition/BpmModelServiceImpl.java | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 7fb85b7835..a8cc848e31 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -24,6 +24,7 @@ public interface ErrorCodeConstants { ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT = new ErrorCode(1_009_002_008, "部署流程失败,原因:BPMN 流程图中,用户任务({})的规则类型不能是【审批人自选】"); // ========== 流程定义 1-009-003-000 ========== ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 9ccc2f2c92..df91df62ec 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; @@ -23,9 +24,7 @@ import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.StartEvent; -import org.flowable.bpmn.model.UserTask; +import org.flowable.bpmn.model.*; import org.flowable.common.engine.impl.db.SuspensionState; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; @@ -48,6 +47,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.convertMap; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy; /** * 流程模型实现:主要进行 Flowable {@link Model} 的维护 @@ -243,7 +243,18 @@ public class BpmModelServiceImpl implements BpmModelService { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验 UserTask 的 name 都配置了 + // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 + List outgoingFlows = startEvent.getOutgoingFlows(); + if (CollUtil.isNotEmpty(outgoingFlows)){ + // 2.1 获取第一个用户节点 + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + // 2.2 获取审批人策略 + Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT, targetFlowElement.getName()); + } + } + // 3. 校验 UserTask 的 name 都配置了 List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); userTasks.forEach(userTask -> { if (StrUtil.isEmpty(userTask.getName())) { From 1b678bd7a990e4c4c2188b212fcf74bcab9ddade Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 5 Mar 2025 21:41:01 +0800 Subject: [PATCH 303/386] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=20=E6=B5=81=E7=A8=8B=E6=A8=A1=E5=9E=8B=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=E7=AC=AC=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=98=AF=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/definition/BpmModelServiceImpl.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/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index df91df62ec..4e3365e070 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -246,7 +246,7 @@ public class BpmModelServiceImpl implements BpmModelService { // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 List outgoingFlows = startEvent.getOutgoingFlows(); if (CollUtil.isNotEmpty(outgoingFlows)){ - // 2.1 获取第一个用户节点 + // 2.1 获取第一个用户任务节点 FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); // 2.2 获取审批人策略 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); From 9ff56403d6782d94a5d1fc75aea8a17b34cecaad Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Mar 2025 07:51:20 +0800 Subject: [PATCH 304/386] =?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=E4=B8=8B=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/ErrorCodeConstants.java | 4 +- .../enums/BpmTaskCandidateStrategyEnum.java | 4 +- .../core/enums/BpmnVariableConstants.java | 2 + .../flowable/core/util/BpmnModelUtils.java | 14 ++--- .../definition/BpmModelServiceImpl.java | 12 ++-- .../task/BpmProcessInstanceService.java | 2 +- .../task/BpmProcessInstanceServiceImpl.java | 61 ++++++++----------- .../bpm/service/task/BpmTaskService.java | 8 +++ .../bpm/service/task/BpmTaskServiceImpl.java | 45 ++++++-------- 9 files changed, 73 insertions(+), 79 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index a8cc848e31..65605142c2 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -24,7 +24,7 @@ public interface ErrorCodeConstants { ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); - ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT = new ErrorCode(1_009_002_008, "部署流程失败,原因:BPMN 流程图中,用户任务({})的规则类型不能是【审批人自选】"); + ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, "部署流程失败,原因:首个任务({})的审批人不能是【审批人自选】"); // ========== 流程定义 1-009-003-000 ========== ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); @@ -41,7 +41,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "任务({})的下一个执行节点审批人未配置"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java index 9b8474ebf0..2a45e3a10c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -24,8 +24,8 @@ public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), POST(22, "岗位"), USER(30, "用户"), - APPROVE_USER_SELECT(34, "审批人,在审批时选择下一个节点的审批人"), // 审批人,在审批时选择下一个节点的审批人 - START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时选择此节点的审批人 + APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人 + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人 START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 START_USER_DEPT_LEADER(37, "发起人部门负责人"), START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index c10d91dcf2..893c4d053c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -27,12 +27,14 @@ public class BpmnVariableConstants { * 流程实例的变量 - 发起用户选择的审批人 Map * * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT */ public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; /** * 流程实例的变量 - 审批人选择的审批人 Map * * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT */ public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; /** diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index c3d033c05c..af5b0852f6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -808,16 +808,16 @@ public class BpmnModelUtils { // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 if (currentElement instanceof ExclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - SequenceFlow matchSequenceFlow = findMatchSequenceFlow((Gateway) currentElement, variables); + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); } } // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 - else if (currentElement instanceof InclusiveGateway) { + else if (currentElement instanceof InclusiveGateway) { // 查找满足条件的 SequenceFlow 路径 - Collection matchSequenceFlows = findMatchSequenceFlows((Gateway) currentElement, variables); + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables); // 遍历满足条件的 SequenceFlow 路径 matchSequenceFlows.forEach( flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); @@ -889,7 +889,7 @@ public class BpmnModelUtils { private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { // 查找满足条件的 SequenceFlow 路径 - SequenceFlow matchSequenceFlow = findMatchSequenceFlow(gateway, variables); + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables); // 遍历满足条件的 SequenceFlow 路径 if (matchSequenceFlow != null) { FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); @@ -906,7 +906,7 @@ public class BpmnModelUtils { * @param variables 流程变量 * @return 符合条件的路径 */ - private static SequenceFlow findMatchSequenceFlow(Gateway gateway, Map variables){ + private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); @@ -932,7 +932,7 @@ public class BpmnModelUtils { private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, Map variables, List nextFlowNodes) { // 查找满足条件的 SequenceFlow 路径集合 - Collection matchSequenceFlows = findMatchSequenceFlows(gateway, variables); + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables); // 遍历满足条件的 SequenceFlow 路径,获取目标节点 matchSequenceFlows.forEach(flow -> { FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); @@ -949,7 +949,7 @@ public class BpmnModelUtils { * @param variables 流程变量 * @return 符合条件的路径 */ - private static Collection findMatchSequenceFlows(Gateway gateway, Map variables) { + private static Collection findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map variables) { // 查找满足条件的 SequenceFlow 路径 Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 4e3365e070..922013ddba 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -243,15 +243,15 @@ public class BpmModelServiceImpl implements BpmModelService { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验第一个用户任务的规则类型是否为 审批人自选,如果是则抛出异常,第一个用户任务的规则类型不允许是审批人自选,因为会出现无审批人的情况 + // 2. 校验第一个用户任务的规则类型是否为“审批人自选”,如果是则抛出异常。原因是,流程发起后,直接进入第一个用户任务,会出现无审批人的情况 List outgoingFlows = startEvent.getOutgoingFlows(); - if (CollUtil.isNotEmpty(outgoingFlows)){ - // 2.1 获取第一个用户任务节点 + // TODO @小北:可能极端情况下,startEvent 后面接了个 serviceTask,接着才是 userTask。。。 + // TODO @小北:simple 因为第一个任务是发起人,可能要找第二个任务??? + if (CollUtil.isNotEmpty(outgoingFlows)) { FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); - // 2.2 获取审批人策略 Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ - throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_RULE_TYPE_NOT_APPROVE_USER_SELECT, targetFlowElement.getName()); + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, targetFlowElement.getName()); } } // 3. 校验 UserTask 的 name 都配置了 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 7df913bf77..6c13416ef4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -97,7 +97,7 @@ public interface BpmProcessInstanceService { BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); /** - * 获取下一个执行节点信息。 + * 获取下一个执行节点信息 * * @param loginUserId 登录人的用户编号 * @param reqVO 请求信息 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 00f40c9fb3..7a8e5a2f5a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -11,7 +11,6 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -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.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -179,9 +178,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); // 合并 DB 和前端传递的流量变量,以前端的为主 - Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(historicVariables)) { - processVariables.putAll(historicVariables); + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); } } if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { @@ -222,9 +220,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, processVariables, activities); - // 3.3 如果是发起动作,activityId为开始节点,不校验审批人自选节点 - if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(),BpmnModelConstants.START_USER_NODE_ID)){ - simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 + // TODO @小北:ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID) 够啦,不用判空 + if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { + simulateActivityNodes.removeIf(node -> + BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } // 4. 拼接最终数据 @@ -234,46 +234,37 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Override public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1.1 校验任务存在 - Task task = taskService.getTask(reqVO.getTaskId()); - if (task == null) { - throw exception(TASK_NOT_EXISTS); - } - // 1.2 校验任务是否由当前用户审批 - if (StrUtil.isNotBlank(task.getAssignee()) - && ObjectUtil.notEqual(loginUserId, NumberUtils.parseLong(task.getAssignee()))) { - throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); - } - // 1.3 校验流程实例存在 + // 1.1 校验任务存在,且是当前用户的 + Task task = taskService.validateTask(loginUserId, reqVO.getTaskId()); + // 1.2 校验流程实例存在 ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); if (instance == null) { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } - // 1.4 校验BpmnModel - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); - if (bpmnModel == null) { - return null; - } - //1.5 流程实例是否存在 HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); if (historicProcessInstance == null) { throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); } + // 1.3 校验BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } - // 2 设置流程变量 + // 2. 设置流程变量 Map processVariables = new HashMap<>(); // 2.1 获取历史中流程变量 - Map historicVariables = historicProcessInstance.getProcessVariables(); - if (CollUtil.isNotEmpty(historicVariables)) { - processVariables.putAll(historicVariables); + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); } // 2.2 合并前端传递的流程变量,以前端为准 if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { processVariables.putAll(reqVO.getProcessVariables()); } + // 3 获取当前任务节点的信息 - FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); // 3.1 获取下一个将要执行的节点集合 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); return convertList(nextFlowNodes, node -> { List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), @@ -290,6 +281,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService .setName(node.getName()) .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + // TODO @小北:先把 candidateUserIds 设置完,然后最后拼接 candidateUsers 信息。这样,如果有多个节点,就不用重复查询啦;类似 buildApprovalDetail 思路; + // TODO 先拼接处 List ActivityNode + // TODO 接着,再起一段,处理 adminUserApi.getUserMap(candidateUserIds)、deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)) .setCandidateUsers(candidateUsers); }); } @@ -745,16 +739,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 - // true,不影响没配置 - // skipExpression 的节点 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点 if (CollUtil.isNotEmpty(startUserSelectAssignees)) { // 设置流程变量,发起人自选审批人 variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); -// // 设置流程变量,审批人自选审批人 -// variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, -// startUserSelectAssignees); } // 3. 创建流程 @@ -796,7 +785,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService return; } - // 2.1 移除掉不是发起人或者审批人自选审批人节点 + // 2.1 移除掉不是发起人自选审批人节点 activityNodes.removeIf(task -> ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index ff8dcca9fe..d95e226d59 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -92,6 +92,14 @@ public interface BpmTaskService { */ List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc); + /** + * 校验任务是否存在,并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + Task validateTask(Long userId, String taskId); + /** * 获取任务 * 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 ef04d799f1..11f219850b 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 @@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; @@ -277,13 +276,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { return query.list(); } - /** - * 校验任务是否存在,并且是否是分配给自己的任务 - * - * @param userId 用户 id - * @param taskId task id - */ - private Task validateTask(Long userId, String taskId) { + @Override + public Task validateTask(Long userId, String taskId) { Task task = validateTaskExist(taskId); // 为什么判断 assignee 非空的情况下? // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 @@ -552,12 +546,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { - // 校验传递的参数中是否为下一个将要执行的任务节点 + // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); + // 完成任务 runtimeService.setVariables(task.getProcessInstanceId(), variables); taskService.complete(task.getId(), variables, true); } else { + // 完成任务 taskService.complete(task.getId()); } @@ -579,54 +575,52 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param processInstance 流程实例 */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, - Map> nextAssignees, ProcessInstance processInstance) { + Map> nextAssignees, ProcessInstance processInstance) { // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 - if (CollUtil.isEmpty(nextAssignees)){ + // TODO @小北:会出现漏选,其实需要的情况哇? + if (CollUtil.isEmpty(nextAssignees)) { return variables; } - // 1. 获取当前任务节点的信息 + // 1. 获取下一个将要执行的节点集合 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - // 2. 获取下一个将要执行的节点集合 List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + + // 2. 循环下一个将要执行的节点集合 Map> processVariables = new HashMap<>(); - // 3. 循环下一个将要执行的节点集合 for (FlowNode nextFlowNode : nextFlowNodes) { - // 3.1 获取下一个将要执行节点中的审批人策略 - Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 3.2 判断节点是否为执行节点,仅校验节点 if (!nextAssignees.containsKey(nextFlowNode.getId())) { throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); } - // 3.3 获取节点中的审批人 List assignees = nextAssignees.get(nextFlowNode.getId()); - // 3.4 流程变量 - // 3.5 如果节点中的审批人策略为 发起人自选 + // 2.1 情况一:如果节点中的审批人策略为 发起人自选 + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if(processVariables == null){ + if (processVariables == null) { processVariables = new HashMap<>(); } List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 如果当前节点已经存在审批人,则不允许覆盖 + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { continue; } // 如果节点存在,但未配置审批人 - if (CollUtil.isEmpty(assignees)){ + if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } - // 3.6 如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())){ + // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { // 如果节点存在,但未配置审批人 if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } // 校验通过的全部节点和审批人 processVariables.put(nextFlowNode.getId(), assignees); + // TODO @小北:是不是要类似情况一的做法,通过 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 拿一下?因为如果 task1 是审批人自选,task2 是审批人自选,看着会覆盖? variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } @@ -1094,6 +1088,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override @Transactional(rollbackFor = Exception.class) + @SuppressWarnings("DataFlowIssue") public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { // 1.1 校验 task 可以被减签 Task task = validateTaskCanSignDelete(reqVO.getId()); From 40b3c49495ddda45f2afe537c8b478bac7d4a9bc Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Mar 2025 08:01:23 +0800 Subject: [PATCH 305/386] =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91=20=E5=90=8C=E4=B8=80=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E5=A4=9A=E4=B8=AA=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=BE=85=E5=8A=9E=E4=BB=BB=E5=8A=A1=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=85=88=E6=9F=A5=E8=AF=A2=E4=BC=A0=E9=80=92=E7=9A=84?= =?UTF-8?q?=20taskId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskService.java | 10 +++--- .../bpm/service/task/BpmTaskServiceImpl.java | 32 +++++++++++++------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index d95e226d59..e99d974356 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -35,16 +35,16 @@ public interface BpmTaskService { PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); /** - * 获得用户(待办)的任务。 - * 1、根据 id 查询待办任务 - * 2、如果任务不存在,获取指定流程下,首个需要处理任务 + * 获得用户(待办)的任务: + * 1. 根据 taskId 查询待办任务 + * 2. 如果任务不存在(或者已审核),获取指定流程下,首个需要处理任务 * * @param userId 用户编号 - * @param id 任务编号 + * @param taskId 任务编号 * @param processInstanceId 流程实例编号 * @return 待办任务 */ - BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId); + BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId); /** * 获得已办的流程任务分页 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 11f219850b..e26b58e793 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 @@ -137,18 +137,18 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override - public BpmTaskRespVO getTodoTask(Long userId, String id, String processInstanceId) { + public BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId) { // 1.1 获取指定的用户待办任务 - Task todoTask = getMyTodoTask(userId, id); + Task todoTask = getMyTodoTask(userId, taskId); + // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 if (todoTask == null) { - // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 - todoTask = getFirstMyTodoTask(userId, processInstanceId); + todoTask = getMyFirstTodoTask(userId, processInstanceId); } if (todoTask == null) { return null; } - // 2.查询该任务的子任务 + // 2. 查询该任务的子任务 List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); // 3. 转换返回 @@ -169,11 +169,18 @@ public class BpmTaskServiceImpl implements BpmTaskService { .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); } - private Task getMyTodoTask(Long userId, String id) { - if (StrUtil.isEmpty(id)) { + /** + * 获得用户指定 taskId 任务编号的“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param taskId 任务编号 + * @return 任务 + */ + private Task getMyTodoTask(Long userId, String taskId) { + if (StrUtil.isEmpty(taskId)) { return null; } - Task task = getTask(id); + Task task = getTask(taskId); if (task == null) { return null; } @@ -183,7 +190,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { return task; } - private Task getFirstMyTodoTask(Long userId, String processInstanceId) { + /** + * 获得用户指定 processInstanceId 流程编号下的首个“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param processInstanceId 流程编号 + * @return 任务 + */ + private Task getMyFirstTodoTask(Long userId, String processInstanceId) { if (processInstanceId == null) { return null; } 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 306/386] =?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 e5c8be2b053e062fdb884505ad1eac27e358a7ae Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Fri, 7 Mar 2025 22:53:35 +0800 Subject: [PATCH 307/386] =?UTF-8?q?review:=20=E4=BC=98=E5=8C=96TODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/task/BpmProcessInstanceServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 7a8e5a2f5a..713b28945c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -221,8 +221,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService processDefinitionInfo, processVariables, activities); // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 - // TODO @小北:ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID) 够啦,不用判空 - if (ObjUtil.isNotNull(reqVO.getActivityId()) && ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { + if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { simulateActivityNodes.removeIf(node -> BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); } From 831970233c63cd50f056b330f0e675102d2dd9c3 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sat, 8 Mar 2025 10:50:22 +0800 Subject: [PATCH 308/386] =?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 309/386] =?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 310/386] =?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 ff9267ad759a819ba97cdd19c5264cb47ad23043 Mon Sep 17 00:00:00 2001 From: puhui999 Date: Sun, 9 Mar 2025 12:53:54 +0800 Subject: [PATCH 311/386] =?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 312/386] =?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 313/386] =?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 4054e5fdec80231d955290e16c8f73d0c4071af2 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Sun, 9 Mar 2025 22:28:13 +0800 Subject: [PATCH 314/386] =?UTF-8?q?fix:=20=E5=8F=91=E8=B5=B7=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E6=8A=A5=E9=94=99=EF=BC=8C=E8=BF=94=E5=9B=9E=E7=9A=84?= =?UTF-8?q?VO=E7=BC=BA=E5=B0=91=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 6 ++++++ .../vo/process/BpmProcessDefinitionRespVO.java | 9 ++------- .../convert/definition/BpmProcessDefinitionConvert.java | 4 ++++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index e00b6d4d3b..a54a5ac716 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -44,6 +44,12 @@ public class BpmModelMetaInfoVO { private Integer formType; @Schema(description = "表单编号", example = "1024") private Long formId; // formType 为 NORMAL 使用,必须非空 + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 8da290440e..71709f7fe8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; @Schema(description = "管理后台 - 流程定义 Response VO") @Data @@ -19,7 +20,7 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "easy-build") private String key; @Schema(description = "流程分类", example = "1") @@ -27,15 +28,9 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程分类名字", example = "请假") private String categoryName; - @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") - private String modelId; - @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java index 1ef8b6f058..d1a5e5a74f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -74,6 +74,10 @@ public interface BpmProcessDefinitionConvert { if (deployment != null) { respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); } + // 图标为null时,处理成空字符串,否则copyTo的to.setIcon( from.getIcon() );会报错 + if (respVO.getIcon() == null) { + respVO.setIcon(""); + } // BpmProcessDefinitionInfoDO if (processDefinitionInfo != null) { copyTo(processDefinitionInfo, respVO); From b6a9b5dda9f518b2cdc180759157b0562cb564a4 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 11 Mar 2025 10:22:21 +0800 Subject: [PATCH 315/386] =?UTF-8?q?fix:=20=E6=A0=A1=E9=AA=8C=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E7=94=A8=E6=88=B7=E4=BB=BB=E5=8A=A1=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E4=B8=BA=E2=80=9C=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA?= =?UTF-8?q?=E9=80=89=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/BpmModelServiceImpl.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 922013ddba..c7df92a27f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -40,6 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -243,24 +244,19 @@ public class BpmModelServiceImpl implements BpmModelService { if (startEvent == null) { throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); } - // 2. 校验第一个用户任务的规则类型是否为“审批人自选”,如果是则抛出异常。原因是,流程发起后,直接进入第一个用户任务,会出现无审批人的情况 - List outgoingFlows = startEvent.getOutgoingFlows(); - // TODO @小北:可能极端情况下,startEvent 后面接了个 serviceTask,接着才是 userTask。。。 - // TODO @小北:simple 因为第一个任务是发起人,可能要找第二个任务??? - if (CollUtil.isNotEmpty(outgoingFlows)) { - FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); - Integer candidateStrategy = parseCandidateStrategy(targetFlowElement); - if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, targetFlowElement.getName()); - } - } - // 3. 校验 UserTask 的 name 都配置了 + // 2. 校验 UserTask 的 name 都配置了 List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); userTasks.forEach(userTask -> { if (StrUtil.isEmpty(userTask.getName())) { throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); } }); + // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” + UserTask userTask = userTasks.get(0); + Integer candidateStrategy = parseCandidateStrategy(userTask); + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTask.getName()); + } } @Override From 26dd8b66702230129ba48a6d87bcda399c06a974 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 11 Mar 2025 17:11:41 +0800 Subject: [PATCH 316/386] =?UTF-8?q?fix=EF=BC=9A=E8=8E=B7=E5=8F=96=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E5=AE=A1=E6=89=B9=E4=BA=BA?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA=E6=97=B6=EF=BC=8C=E8=BF=94=E5=9B=9Enew=20Has?= =?UTF-8?q?hMap<>()=EF=BC=8C=E9=81=BF=E5=85=8D=E4=B8=8B=E7=BA=A7=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=A9=BA=E6=8C=87=E9=92=88=20review:=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=AE=A1=E6=89=B9=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=90=88=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/FlowableUtils.java | 6 +- .../bpm/service/task/BpmTaskServiceImpl.java | 65 +++++++++---------- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index fe83495e35..4178638b5a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -194,7 +194,7 @@ public class FlowableUtils { @SuppressWarnings("unchecked") public static Map> getStartUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); @@ -214,12 +214,12 @@ public class FlowableUtils { * 获得流程实例的审批用户选择的下一个节点的审批人 Map * * @param processVariables 流程变量 - * @return 审批用户选择的下一个节点的审批人Map Map + * @return 审批用户选择的下一个节点的审批人Map */ @SuppressWarnings("unchecked") public static Map> getApproveUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); 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 e26b58e793..09eea1e52c 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 @@ -558,18 +558,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 - // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 - if (CollUtil.isNotEmpty(reqVO.getVariables())) { - // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 - Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), - bpmnModel, reqVO.getNextAssignees(), instance); - // 完成任务 - runtimeService.setVariables(task.getProcessInstanceId(), variables); - taskService.complete(task.getId(), variables, true); - } else { - // 完成任务 - taskService.complete(task.getId()); - } + // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + bpmnModel, reqVO.getNextAssignees(), instance); + // 完成任务 + runtimeService.setVariables(task.getProcessInstanceId(), variables); + taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); @@ -590,51 +584,50 @@ public class BpmTaskServiceImpl implements BpmTaskService { */ private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, Map> nextAssignees, ProcessInstance processInstance) { - // 下一个节点参数为空,不做处理,表示流程正常流转,无需选择下一个节点的审判人 - // TODO @小北:会出现漏选,其实需要的情况哇? - if (CollUtil.isEmpty(nextAssignees)) { - return variables; - } // 1. 获取下一个将要执行的节点集合 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 2. 循环下一个将要执行的节点集合 - Map> processVariables = new HashMap<>(); + Map> processVariables; for (FlowNode nextFlowNode : nextFlowNodes) { - if (!nextAssignees.containsKey(nextFlowNode.getId())) { - throw exception(TASK_TARGET_NODE_NOT_EXISTS, nextFlowNode.getName()); - } - List assignees = nextAssignees.get(nextFlowNode.getId()); - // 2.1 情况一:如果节点中的审批人策略为 发起人自选 + // 获取任务节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 2.1 情况一:如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { - processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (processVariables == null) { - processVariables = new HashMap<>(); - } - List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - if (CollUtil.isNotEmpty(startUserSelectAssignee)) { - continue; - } // 如果节点存在,但未配置审批人 + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isNotEmpty(processVariables)) { + List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(startUserSelectAssignee)) { + continue; + } + } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), assignees); + processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { // 如果节点存在,但未配置审批人 + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } + processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + if (CollUtil.isNotEmpty(processVariables)) { + List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { + continue; + } + } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), assignees); - // TODO @小北:是不是要类似情况一的做法,通过 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 拿一下?因为如果 task1 是审批人自选,task2 是审批人自选,看着会覆盖? + processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } From 494b80d1ebae4099d719f671a4cdfe91313f5f16 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Tue, 11 Mar 2025 17:34:13 +0800 Subject: [PATCH 317/386] =?UTF-8?q?review:=20=E4=BC=98=E5=8C=96=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E6=97=B6=EF=BC=8C=E6=A0=A1=E9=AA=8C=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C=E6=98=AF=E5=90=A6=E5=90=88?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 13 ++++++++++--- .../framework/flowable/core/util/FlowableUtils.java | 6 +++--- .../module/bpm/service/task/BpmTaskServiceImpl.java | 8 ++++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index af5b0852f6..c471826306 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -907,9 +907,16 @@ public class BpmnModelUtils { * @return 符合条件的路径 */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); + // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值, + SequenceFlow matchSequenceFlow; + if (CollUtil.isNotEmpty(variables)){ + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + }else { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); + } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index 4178638b5a..fe83495e35 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -194,7 +194,7 @@ public class FlowableUtils { @SuppressWarnings("unchecked") public static Map> getStartUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return new HashMap<>(); + return null; } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); @@ -214,12 +214,12 @@ public class FlowableUtils { * 获得流程实例的审批用户选择的下一个节点的审批人 Map * * @param processVariables 流程变量 - * @return 审批用户选择的下一个节点的审批人Map + * @return 审批用户选择的下一个节点的审批人Map Map */ @SuppressWarnings("unchecked") public static Map> getApproveUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return new HashMap<>(); + return null; } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); 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 09eea1e52c..0bfd6b0c6c 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 @@ -600,7 +600,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isNotEmpty(processVariables)) { + if (processVariables == null){ + processVariables = new HashMap<>(); + }else { List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(startUserSelectAssignee)) { @@ -619,7 +621,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (CollUtil.isNotEmpty(processVariables)) { + if (processVariables == null){ + processVariables = new HashMap<>(); + }else { List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { From 27ae2a4761739a21feca8bad24544fadeff9d227 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 21:23:19 +0800 Subject: [PATCH 318/386] =?UTF-8?q?review:=20=E7=88=B6=E5=AD=90=E7=B1=BB?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 3 +-- .../admin/definition/vo/model/BpmModelSaveReqVO.java | 3 +++ .../definition/vo/process/BpmProcessDefinitionRespVO.java | 8 +++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index a54a5ac716..97a8a3ca7b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -44,8 +44,7 @@ public class BpmModelMetaInfoVO { private Integer formType; @Schema(description = "表单编号", example = "1024") private Long formId; // formType 为 NORMAL 使用,必须非空 - @Schema(description = "表单名字", example = "请假表单") - private String formName; + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) private String formConf; @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index 7e32596524..bcb193df1e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -21,6 +21,9 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @NotEmpty(message = "流程名称不能为空") private String name; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "流程分类", example = "1") private String category; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 71709f7fe8..81bddf7b11 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -20,7 +20,10 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "easy-build") + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") private String key; @Schema(description = "流程分类", example = "1") @@ -31,6 +34,9 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String modelId; + @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 From 2123d7c06717113a83aaf9fccf70b71559f8ad0a Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 21:28:29 +0800 Subject: [PATCH 319/386] =?UTF-8?q?remove:=20=E5=88=A0=E9=99=A4=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/convert/definition/BpmProcessDefinitionConvert.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java index d1a5e5a74f..1ef8b6f058 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -74,10 +74,6 @@ public interface BpmProcessDefinitionConvert { if (deployment != null) { respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); } - // 图标为null时,处理成空字符串,否则copyTo的to.setIcon( from.getIcon() );会报错 - if (respVO.getIcon() == null) { - respVO.setIcon(""); - } // BpmProcessDefinitionInfoDO if (processDefinitionInfo != null) { copyTo(processDefinitionInfo, respVO); From cc61bb1a61e837ac62dee76045d3cd77ed4f303b Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 22:37:23 +0800 Subject: [PATCH 320/386] =?UTF-8?q?review=EF=BC=9A=20=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E8=AE=BE=E8=AE=A1=E5=99=A8=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E7=94=A8=E6=88=B7=E4=BB=BB=E5=8A=A1=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E7=9A=84=E8=A7=84=E5=88=99=E7=B1=BB=E5=9E=8B=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=BA=E2=80=9C=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/BpmModelServiceImpl.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index c7df92a27f..7a737d953a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -40,10 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import java.util.ArrayList; -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.convertMap; @@ -210,11 +207,11 @@ public class BpmModelServiceImpl implements BpmModelService { public void deployModel(Long userId, String id) { // 1.1 校验流程模型存在 Model model = validateModelManager(id, userId); + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); // 1.2 校验流程图 byte[] bpmnBytes = getModelBpmnXML(model.getId()); - validateBpmnXml(bpmnBytes); + validateBpmnXml(bpmnBytes, metaInfo.getType()); // 1.3 校验表单已配 - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); BpmFormDO form = validateFormConfig(metaInfo); // 1.4 校验任务分配规则已配置 taskCandidateInvoker.validateBpmnConfig(bpmnBytes); @@ -234,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService { repositoryService.saveModel(model); } - private void validateBpmnXml(byte[] bpmnBytes) { + private void validateBpmnXml(byte[] bpmnBytes, int type) { BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); if (bpmnModel == null) { throw exception(MODEL_NOT_EXISTS); @@ -252,10 +249,14 @@ public class BpmModelServiceImpl implements BpmModelService { } }); // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” - UserTask userTask = userTasks.get(0); - Integer candidateStrategy = parseCandidateStrategy(userTask); + Map userTaskMap = new HashMap<>(); + // BPMN 设计器,校验第一个用户任务节点 + userTaskMap.put(BpmModelTypeEnum.BPMN.getType(), userTasks.get(0)); + // SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 + userTaskMap.put(BpmModelTypeEnum.SIMPLE.getType(), userTasks.get(1)); + Integer candidateStrategy = parseCandidateStrategy(userTaskMap.get(type)); if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTask.getName()); + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTaskMap.get(type).getName()); } } From 42eb89b2433b8197602fb5441078d50c40fbc44d Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Tue, 11 Mar 2025 22:40:53 +0800 Subject: [PATCH 321/386] =?UTF-8?q?review=EF=BC=9A=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E6=A0=A1=E9=AA=8C=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E4=BA=BA=EF=BC=8C=E6=98=AF=E5=90=A6=E5=90=88?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/bpm/service/task/BpmTaskServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 0bfd6b0c6c..eab70f921b 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 @@ -610,7 +610,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 @@ -631,7 +631,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } } // 校验通过的全部节点和审批人 - processVariables.put(nextFlowNode.getId(), nextAssignees.get(nextFlowNode.getId())); + processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } } From 23844b930cdafa34edf5f45fd5b911cfb7b65eed Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 12 Mar 2025 22:42:32 +0800 Subject: [PATCH 322/386] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E8=8A=82=E7=82=B9=E8=A1=A8=E5=8D=95=E6=97=A0=E5=8F=AF?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=AD=97=E6=AE=B5=E6=97=B6=EF=BC=8Cvariables?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F=E5=80=BC=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=EF=BC=8C=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=B5=81=E8=BD=AC?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 9 +-------- .../bpm/service/task/BpmTaskServiceImpl.java | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index c471826306..1cccf18f04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -907,16 +907,9 @@ public class BpmnModelUtils { * @return 符合条件的路径 */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { - // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值, - SequenceFlow matchSequenceFlow; - if (CollUtil.isNotEmpty(variables)){ - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); - }else { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); 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 eab70f921b..aeabde6f03 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 @@ -558,12 +558,21 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); // 2.3 调用 BPM complete 去完成任务 + // 如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时variables一定会为空 + // 场景一:A节点发起,B节点表单无可编辑字段,审批通过时,C节点需要流程变量获取下一个执行节点,但因为B节点无可编辑的字段,variables为空,流程可能出现问题 + // 场景二:A节点发起,B节点只有某一个字段可编辑(比如day),但C节点需要多个节点(比如workday,在发起时填写,因为B节点只有day的编辑权限,在审批后。variables会缺少work的值) + // 历史中的变量值 + Map variables = new HashMap<>(instance.getProcessVariables()); + // 如果变量值不为空,则覆盖历史变量 + if (CollUtil.isNotEmpty(reqVO.getVariables())) { + variables.putAll(reqVO.getVariables()); + } // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 - Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + Map resVariables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), variables, bpmnModel, reqVO.getNextAssignees(), instance); // 完成任务 - runtimeService.setVariables(task.getProcessInstanceId(), variables); - taskService.complete(task.getId(), variables, true); + runtimeService.setVariables(task.getProcessInstanceId(), resVariables); + taskService.complete(task.getId(), resVariables, true); // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); From c2789f628c2c053b50986d25de057cb2efcc6953 Mon Sep 17 00:00:00 2001 From: smallNorthLee <18210040298@163.com> Date: Wed, 12 Mar 2025 22:46:31 +0800 Subject: [PATCH 323/386] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E8=8A=82=E7=82=B9=E8=A1=A8=E5=8D=95=E6=97=A0=E5=8F=AF?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=AD=97=E6=AE=B5=E6=97=B6=EF=BC=8Cvariables?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F=E5=80=BC=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=EF=BC=8C=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=B5=81=E8=BD=AC?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/task/BpmTaskServiceImpl.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) 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 aeabde6f03..60410d7769 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 @@ -557,22 +557,27 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - // 2.3 调用 BPM complete 去完成任务 + // 如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时variables一定会为空 // 场景一:A节点发起,B节点表单无可编辑字段,审批通过时,C节点需要流程变量获取下一个执行节点,但因为B节点无可编辑的字段,variables为空,流程可能出现问题 // 场景二:A节点发起,B节点只有某一个字段可编辑(比如day),但C节点需要多个节点(比如workday,在发起时填写,因为B节点只有day的编辑权限,在审批后。variables会缺少work的值) // 历史中的变量值 - Map variables = new HashMap<>(instance.getProcessVariables()); - // 如果变量值不为空,则覆盖历史变量 + // 3.1 设置流程变量 + Map processVariables = new HashMap<>(); + // 3.2 获取历史中流程变量 + if (CollUtil.isNotEmpty(instance.getProcessVariables())) { + processVariables.putAll(instance.getProcessVariables()); + } + // 3.3 合并前端传递的流程变量,以前端为准 if (CollUtil.isNotEmpty(reqVO.getVariables())) { - variables.putAll(reqVO.getVariables()); + processVariables.putAll(reqVO.getVariables()); } // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 - Map resVariables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), variables, + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, bpmnModel, reqVO.getNextAssignees(), instance); - // 完成任务 - runtimeService.setVariables(task.getProcessInstanceId(), resVariables); - taskService.complete(task.getId(), resVariables, true); + // 3.4 调用 BPM complete 去完成任务 + runtimeService.setVariables(task.getProcessInstanceId(), variables); + taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); From 9ae18fe53d68ed2331ff9d31f28874ca7db81282 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 06:25:16 +0800 Subject: [PATCH 324/386] =?UTF-8?q?=E3=80=90=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E3=80=91BPM=EF=BC=9AformConf=E3=80=81formFields=20?= =?UTF-8?q?=E5=9C=A8=20BpmProcessDefinitionRespVO=20=E7=BC=BA=E5=B0=91?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E5=8F=91=E8=B5=B7=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 4 ---- .../admin/definition/vo/model/BpmModelSaveReqVO.java | 3 --- .../vo/process/BpmProcessDefinitionRespVO.java | 10 +++++++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 97a8a3ca7b..842f396d47 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -45,10 +45,6 @@ public class BpmModelMetaInfoVO { @Schema(description = "表单编号", example = "1024") private Long formId; // formType 为 NORMAL 使用,必须非空 - @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private String formConf; - @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private List formFields; @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index bcb193df1e..7e32596524 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -21,9 +21,6 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @NotEmpty(message = "流程名称不能为空") private String name; - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "流程分类", example = "1") private String category; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 81bddf7b11..0ec737174a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -20,9 +20,6 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") private String key; @@ -37,6 +34,13 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") private String modelId; + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer suspensionState; // 参见 SuspensionState 枚举 From 24261cf767fea0e71d813e590e12d3debb7b97db Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 06:44:41 +0800 Subject: [PATCH 325/386] =?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=AE=A1=E7=AE=97=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E4=BA=BA=EF=BC=88=E5=AE=A1=E6=89=B9=E4=BA=BA=E8=87=AA=E9=80=89?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 5 +-- .../flowable/core/util/FlowableUtils.java | 4 +-- .../definition/BpmModelServiceImpl.java | 3 +- .../bpm/service/task/BpmTaskServiceImpl.java | 34 +++++++++---------- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index c471826306..2ec26b407e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -908,12 +908,13 @@ public class BpmnModelUtils { */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值, + // TODO @小北:是不是还是保证,编辑的时候,如果计算下一个节点,还是 variables 是完整体?而不是空的!!!(可以微信讨论下) SequenceFlow matchSequenceFlow; - if (CollUtil.isNotEmpty(variables)){ + if (CollUtil.isNotEmpty(variables)) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); - }else { + } else { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java index fe83495e35..67c24bb9f4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -194,7 +194,7 @@ public class FlowableUtils { @SuppressWarnings("unchecked") public static Map> getStartUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); @@ -219,7 +219,7 @@ public class FlowableUtils { @SuppressWarnings("unchecked") public static Map> getApproveUserSelectAssignees(Map processVariables) { if (processVariables == null) { - return null; + return new HashMap<>(); } return (Map>) processVariables.get( BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 7a737d953a..6f3162683f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -231,7 +231,7 @@ public class BpmModelServiceImpl implements BpmModelService { repositoryService.saveModel(model); } - private void validateBpmnXml(byte[] bpmnBytes, int type) { + private void validateBpmnXml(byte[] bpmnBytes, Integer type) { BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); if (bpmnModel == null) { throw exception(MODEL_NOT_EXISTS); @@ -248,6 +248,7 @@ public class BpmModelServiceImpl implements BpmModelService { throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); } }); + // TODO @小北:是不是可以 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);然后,最好判空。。。极端情况下,没 usertask ,哈哈哈哈。 // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” Map userTaskMap = new HashMap<>(); // BPMN 设计器,校验第一个用户任务节点 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 eab70f921b..f96d0cb392 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 @@ -557,12 +557,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - // 2.3 调用 BPM complete 去完成任务 - // 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + // 2.3 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), bpmnModel, reqVO.getNextAssignees(), instance); - // 完成任务 runtimeService.setVariables(task.getProcessInstanceId(), variables); + // 2.4 调用 BPM complete 去完成任务 taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 @@ -587,10 +586,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 1. 获取下一个将要执行的节点集合 FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - // 2. 循环下一个将要执行的节点集合 + + // 2. 校验选择的下一个节点的审批人,是否合法 Map> processVariables; for (FlowNode nextFlowNode : nextFlowNodes) { - // 获取任务节点中的审批人策略 Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); // 2.1 情况一:如果节点中的审批人策略为 发起人自选 if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { @@ -600,16 +599,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (processVariables == null){ - processVariables = new HashMap<>(); - }else { - List startUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - if (CollUtil.isNotEmpty(startUserSelectAssignee)) { - continue; - } + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + // TODO @小北:【不用改】通过 if return,让逻辑更简洁一点;虽然会多判断一次 processVariables,但是 if else 层级更少。 + if (processVariables != null + && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) { + continue; + } + // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES + if (processVariables == null) { + processVariables = new HashMap<>(); } - // 校验通过的全部节点和审批人 processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); } @@ -621,16 +620,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); } processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - if (processVariables == null){ + if (processVariables == null) { processVariables = new HashMap<>(); - }else { + } else { List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + // TODO @小北:这种,应该可以覆盖呢。 if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { continue; } } - // 校验通过的全部节点和审批人 + // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES processVariables.put(nextFlowNode.getId(), assignees); variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); } From 569d6514816e373c9fa2ec66295707a2b7a7c89e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 13 Mar 2025 08:17:31 +0800 Subject: [PATCH 326/386] =?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 327/386] =?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 575e7a38f37e32d28e0488c523cf55801f2a694f Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 13 Mar 2025 09:38:15 +0800 Subject: [PATCH 328/386] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E8=8A=82=E7=82=B9=E8=A1=A8=E5=8D=95=E6=97=A0=E5=8F=AF?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=AD=97=E6=AE=B5=E6=97=B6=EF=BC=8Cvariables?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F=E5=80=BC=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=EF=BC=8C=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=B5=81=E8=BD=AC?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flowable/core/util/BpmnModelUtils.java | 10 +------ .../bpm/service/task/BpmTaskServiceImpl.java | 30 +++++++++++-------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 2ec26b407e..1cccf18f04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -907,17 +907,9 @@ public class BpmnModelUtils { * @return 符合条件的路径 */ private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { - // TODO 表单无可编辑字段时variables为空,流程走向会出现问题,比如流程审批过程中无需要修改的字段值, - // TODO @小北:是不是还是保证,编辑的时候,如果计算下一个节点,还是 variables 是完整体?而不是空的!!!(可以微信讨论下) - SequenceFlow matchSequenceFlow; - if (CollUtil.isNotEmpty(variables)) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) && (evalConditionExpress(variables, flow.getConditionExpression()))); - } else { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())); - } if (matchSequenceFlow == null) { matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); 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..b15a59c540 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 @@ -557,11 +557,24 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - // 2.3 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 - Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), reqVO.getVariables(), + // 如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时variables一定会为空 + // 场景一:A节点发起,B节点表单无可编辑字段,审批通过时,C节点需要流程变量获取下一个执行节点,但因为B节点无可编辑的字段,variables为空,流程可能出现问题 + // 场景二:A节点发起,B节点只有某一个字段可编辑(比如day),但C节点需要多个节点(比如workday,在发起时填写,因为B节点只有day的编辑权限,在审批后。variables会缺少work的值) + // 3.1 设置流程变量 + Map processVariables = new HashMap<>(); + // 3.2 获取历史中流程变量 + if (CollUtil.isNotEmpty(instance.getProcessVariables())) { + processVariables.putAll(instance.getProcessVariables()); + } + // 3.3 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getVariables())) { + processVariables.putAll(reqVO.getVariables()); + } + // 3.4 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, bpmnModel, reqVO.getNextAssignees(), instance); runtimeService.setVariables(task.getProcessInstanceId(), variables); - // 2.4 调用 BPM complete 去完成任务 + // 4 调用 BPM complete 去完成任务 taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 @@ -600,9 +613,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - // TODO @小北:【不用改】通过 if return,让逻辑更简洁一点;虽然会多判断一次 processVariables,但是 if else 层级更少。 - if (processVariables != null - && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) { + if (processVariables != null && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) { continue; } // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES @@ -622,13 +633,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); if (processVariables == null) { processVariables = new HashMap<>(); - } else { - List approveUserSelectAssignee = processVariables.get(nextFlowNode.getId()); - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - // TODO @小北:这种,应该可以覆盖呢。 - if (CollUtil.isNotEmpty(approveUserSelectAssignee)) { - continue; - } } // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES processVariables.put(nextFlowNode.getId(), assignees); From c9690e144c05a753c258fe84bb5b507f605d9f7a Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 13 Mar 2025 16:05:13 +0800 Subject: [PATCH 329/386] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E8=8A=82=E7=82=B9=E8=A1=A8=E5=8D=95=E6=97=A0=E5=8F=AF?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E5=AD=97=E6=AE=B5=E6=97=B6=EF=BC=8Cvariables?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F=E5=80=BC=E4=B8=BA=E7=A9=BA?= =?UTF-8?q?=EF=BC=8C=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=B5=81=E8=BD=AC?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/framework/flowable/core/util/BpmnModelUtils.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 1cccf18f04..fdfb724442 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -21,6 +21,7 @@ import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.javax.el.PropertyNotFoundException; import org.flowable.common.engine.impl.util.io.BytesStreamSource; import org.flowable.engine.impl.el.FixedValue; @@ -1006,6 +1007,11 @@ public class BpmnModelUtils { Object result = FlowableUtils.getExpressionValue(variables, expression); return Boolean.TRUE.equals(result); } catch (FlowableException ex) { + // TODO @芋艿 临时方案解决流程变量中不包含条件表达式时报错问题,如果expression 的计算,可能不依赖于 variables,getExpressionValue方法应该需要重构 + if (ex.getCause() instanceof PropertyNotFoundException){ + log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); + return Boolean.FALSE; + } // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); return Boolean.FALSE; From be608b26e674ecaa0c79fc3ac239cb38fd1ba4f3 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Thu, 13 Mar 2025 16:22:58 +0800 Subject: [PATCH 330/386] =?UTF-8?q?review:=20=E4=BB=A3=E7=A0=81=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/service/definition/BpmModelServiceImpl.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 6f3162683f..a441803218 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -248,16 +248,11 @@ public class BpmModelServiceImpl implements BpmModelService { throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); } }); - // TODO @小北:是不是可以 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1);然后,最好判空。。。极端情况下,没 usertask ,哈哈哈哈。 - // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选” - Map userTaskMap = new HashMap<>(); - // BPMN 设计器,校验第一个用户任务节点 - userTaskMap.put(BpmModelTypeEnum.BPMN.getType(), userTasks.get(0)); - // SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 - userTaskMap.put(BpmModelTypeEnum.SIMPLE.getType(), userTasks.get(1)); - Integer candidateStrategy = parseCandidateStrategy(userTaskMap.get(type)); + // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 + UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); + Integer candidateStrategy = parseCandidateStrategy(firUserTask); if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, userTaskMap.get(type).getName()); + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName()); } } 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 331/386] =?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 332/386] =?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 e114a354513140820e3d9add68d22804217a54a6 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 14 Mar 2025 09:29:58 +0800 Subject: [PATCH 333/386] =?UTF-8?q?feat:=20=E6=B5=81=E7=A8=8B=E5=89=8D?= =?UTF-8?q?=E5=90=8E=E7=BD=AE=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/model/BpmModelMetaInfoVO.java | 36 ++++ .../BpmProcessDefinitionInfoDO.java | 12 ++ .../BpmProcessInstanceEventListener.java | 6 + .../core/util/BpmHttpRequestUtils.java | 158 ++++++++++++++++++ .../flowable/core/util/SimpleModelUtils.java | 24 --- .../task/BpmProcessInstanceService.java | 6 + .../task/BpmProcessInstanceServiceImpl.java | 95 ++++++++--- .../task/listener/BpmUserTaskListener.java | 78 ++++----- .../http/BpmAbstractHttpRequestTrigger.java | 56 ------- .../trigger/http/BpmHttpCallbackTrigger.java | 28 ++-- .../http/BpmSyncHttpRequestTrigger.java | 73 ++------ 11 files changed, 348 insertions(+), 224 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 842f396d47..9fc52fdc0c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; +import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; @@ -80,6 +82,12 @@ public class BpmModelMetaInfoVO { @Schema(description = "摘要设置", example = "{}") private SummarySetting summarySetting; + @Schema(description = "流程前置通知设置", example = "{}") + private HttpRequestSetting PreProcessNotifySetting; + + @Schema(description = "流程后置通知设置", example = "{}") + private HttpRequestSetting PostProcessNotifySetting; + @Schema(description = "流程 ID 规则") @Data @Valid @@ -132,4 +140,32 @@ public class BpmModelMetaInfoVO { } + @Schema(description = "http 请求通知设置", example = "{}") + @Data + public static class HttpRequestSetting { + + @Schema(description = "请求路径", example = "http://127.0.0.1") + @NotEmpty(message = "请求 URL 不能为空") + @URL(message = "请求 URL 格式不正确") + private String url; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List header; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List body; + + /** + * 请求返回处理设置,用于修改流程表单值 + *

+ * key:表示要修改的流程表单字段名(name) + * value:接口返回的字段名 + */ + @Schema(description = "请求返回处理设置", example = "[]") + private List> response; + + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index 34dfbece3a..cc4c697e6f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -189,4 +189,16 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.SummarySetting summarySetting; + /** + * 流程前置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting PreProcessNotifySetting; + + /** + * 流程后置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting PostProcessNotifySetting; + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java index 5251aea64f..ba2aaa6bcb 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -22,6 +22,7 @@ import java.util.Set; public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.PROCESS_CREATED) .add(FlowableEngineEventType.PROCESS_COMPLETED) .add(FlowableEngineEventType.PROCESS_CANCELLED) .build(); @@ -34,6 +35,11 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent super(PROCESS_INSTANCE_EVENTS); } + @Override + protected void processCreated(FlowableEngineEntityEvent event) { + processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); + } + @Override protected void processCompleted(FlowableEngineEntityEvent event) { processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java new file mode 100644 index 0000000000..74a8da09f5 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -0,0 +1,158 @@ +package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +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.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; + +/** + * 工作流发起 HTTP 请求工具类 + * + * @author 芋道源码 + */ +@Slf4j +public class BpmHttpRequestUtils { + + public static void executeBpmHttpRequest(ProcessInstance processInstance, + String url, + List headerParam, + List bodyParam, + Boolean handleResponse, + List> response, + RestTemplate restTemplate, + BpmProcessInstanceService processInstanceService) { + + // 1.1 设置请求头 + MultiValueMap headers = BpmHttpRequestUtils.buildHttpHeaders(processInstance, headerParam); + // 1.2 设置请求体 + MultiValueMap body = BpmHttpRequestUtils.buildHttpBody(processInstance, bodyParam); + + // 2. 发起请求 + ResponseEntity responseEntity = BpmHttpRequestUtils.sendHttpRequest(url, headers, body, restTemplate); + + // 3. 处理返回 + if (Boolean.TRUE.equals(handleResponse)) { + // 3.1 判断是否需要解析返回值 + if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) + || !responseEntity.getStatusCode().is2xxSuccessful() + || CollUtil.isEmpty(response)) { + return; + } + // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 + CommonResult> respResult = JsonUtils.parseObjectQuietly( + responseEntity.getBody(), new TypeReference<>() { + }); + if (respResult == null || !respResult.isSuccess()) { + return; + } + // 3.3 获取需要更新的流程变量 + Map updateVariables = BpmHttpRequestUtils.getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + // 3.4 更新流程变量 + if (CollUtil.isNotEmpty(updateVariables)) { + processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); + } + } + + } + + public static ResponseEntity sendHttpRequest(String url, + MultiValueMap headers, + MultiValueMap body, + RestTemplate restTemplate) { + // 3. 发起请求 + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + } catch (RestClientException e) { + log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); + } + return responseEntity; + } + + public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, + List headerSettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); + addHttpRequestParam(headers, headerSettings, processVariables); + return headers; + } + + public static MultiValueMap buildHttpBody(ProcessInstance processInstance, + List bodySettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap body = new LinkedMultiValueMap<>(); + addHttpRequestParam(body, bodySettings, processVariables); + body.add("processInstanceId", processInstance.getId()); + return body; + } + + /** + * 从请求返回值获取需要更新的流程变量 + * + * @param result 请求返回结果 + * @param responseSettings 返回设置 + * @return 需要更新的流程变量 + */ + public static Map getNeedUpdatedVariablesFromResponse(Map result, + List> responseSettings) { + Map updateVariables = new HashMap<>(); + if (CollUtil.isEmpty(result)) { + return updateVariables; + } + responseSettings.forEach(responseSetting -> { + if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { + updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); + } + }); + return updateVariables; + } + + /** + * 添加 HTTP 请求参数。请求头或者请求体 + * + * @param params HTTP 请求参数 + * @param paramSettings HTTP 请求参数设置 + * @param processVariables 流程变量 + */ + public static void addHttpRequestParam(MultiValueMap params, + List paramSettings, + Map processVariables) { + if (CollUtil.isEmpty(paramSettings)) { + return; + } + paramSettings.forEach(item -> { + if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { + params.add(item.getKey(), item.getValue()); + } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { + params.add(item.getKey(), processVariables.get(item.getValue()).toString()); + } + }); + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 0060d0e9c5..3b5bad52c7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -21,7 +21,6 @@ import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.engine.delegate.ExecutionListener; import org.flowable.engine.delegate.TaskListener; -import org.springframework.util.MultiValueMap; import java.util.*; @@ -1002,27 +1001,4 @@ public class SimpleModelUtils { return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); } - // TODO @芋艿:【高】要不要优化下,抽个 HttpUtils - - /** - * 添加 HTTP 请求参数。请求头或者请求体 - * - * @param params HTTP 请求参数 - * @param paramSettings HTTP 请求参数设置 - * @param processVariables 流程变量 - */ - public static void addHttpRequestParam(MultiValueMap params, - List paramSettings, - Map processVariables) { - if (CollUtil.isEmpty(paramSettings)) { - return; - } - paramSettings.forEach(item -> { - if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { - params.add(item.getKey(), item.getValue()); - } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { - params.add(item.getKey(), processVariables.get(item.getValue()).toString()); - } - }); - } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java index 6c13416ef4..abba2245e2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java @@ -182,4 +182,10 @@ public interface BpmProcessInstanceService { */ void processProcessInstanceCompleted(ProcessInstance instance); + /** + * 处理 ProcessInstance 开始事件,例如说:流程前置通知 + * + * @param instance 流程任务 + */ + void processProcessInstanceCreated(ProcessInstance instance); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 713b28945c..4ca5a189ff 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -34,6 +34,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; @@ -62,6 +63,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import org.springframework.web.client.RestTemplate; import java.util.*; @@ -120,6 +122,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Resource private BpmProcessIdRedisDAO processIdRedisDAO; + @Resource + private RestTemplate restTemplate; + // ========== Query 查询相关方法 ========== @Override @@ -148,7 +153,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { + String activityId, String taskId) { // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) @@ -351,15 +356,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 主要是,拼接审批人的用户信息、部门信息 */ private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { // 1. 获取所有需要读取用户信息的 userIds List approveNodes = newArrayList( asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); @@ -381,9 +386,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【已结束】的活动节点们 */ private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities // 的话,它无法成为一个节点 @@ -451,11 +456,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【进行中】的活动节点们 */ private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { // 构建运行中的任务、子流程,基于 activityId 分组 List runActivities = filterList(activities, activity -> activity.getEndTime() == null && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); @@ -516,9 +521,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService * 获得【预测(未来)】的活动节点们 */ private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -540,8 +545,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { @@ -585,8 +590,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { if (runActivityIds.contains(node.getId())) { return null; } @@ -902,6 +907,46 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); + + // 4. 流程后置通知 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNotNull(processDefinitionInfo) && + ObjUtil.isNotNull(processDefinitionInfo.getPostProcessNotifySetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPostProcessNotifySetting(); + + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse(), + restTemplate, + this); + } + } + }); + } + + @Override + public void processProcessInstanceCreated(ProcessInstance instance) { + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(instance.getTenantId(), () -> { + // 流程前置通知 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNotNull(processDefinitionInfo) && + ObjUtil.isNotNull(processDefinitionInfo.getPreProcessNotifySetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPreProcessNotifySetting(); + + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse(), + restTemplate, + this); + } }); } } 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..d02844353d 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 @@ -1,29 +1,20 @@ package cn.iocoder.yudao.module.bpm.service.task.listener; -import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.delegate.TaskListener; -import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.service.delegate.DelegateTask; import org.springframework.context.annotation.Scope; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; -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; /** @@ -50,46 +41,35 @@ public class BpmUserTaskListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { // 1. 获取所需基础信息 - HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId()); + ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId()); BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); - // 2. 获取请求头和请求体 - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - MultiValueMap body = new LinkedMultiValueMap<>(); - SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables); - SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables); - // 2.1 请求头默认参数 - if (StrUtil.isNotEmpty(delegateTask.getTenantId())) { - headers.add(HEADER_TENANT_ID, delegateTask.getTenantId()); - } - // 2.2 请求体默认参数 + // 2. 发起请求 // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; - body.add("processInstanceId", delegateTask.getProcessInstanceId()); - body.add("assignee", delegateTask.getAssignee()); - body.add("taskDefinitionKey", delegateTask.getTaskDefinitionKey()); - body.add("taskId", delegateTask.getId()); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getProcessInstanceId())); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getAssignee())); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getTaskDefinitionKey())); + listenerHandler.getBody() + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(delegateTask.getId())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + listenerHandler.getPath(), + listenerHandler.getHeader(), + listenerHandler.getBody(), + false, null, + restTemplate, + processInstanceService); - // 3. 异步发起请求 - // TODO @芋艿:确认要同步,还是异步 - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - try { - ResponseEntity responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST, - requestEntity, String.class); - log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]", - DELEGATE_EXPRESSION, - delegateTask.getEventName(), - headers, - body, - responseEntity); - } catch (RestClientException e) { - log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]", - DELEGATE_EXPRESSION, - delegateTask.getEventName(), - headers, - body, - e.getMessage()); - } - // 4. 是否需要后续操作?TODO 芋艿:待定! + // 3. 是否需要后续操作?TODO 芋艿:待定! } } \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java index 0e5e7276f7..b1d81bc146 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java @@ -1,25 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http; -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils; import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger; -import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -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.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; /** * BPM 发送 HTTP 请求触发器抽象类 @@ -29,42 +11,4 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTA @Slf4j public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { - @Resource - private RestTemplate restTemplate; - - protected ResponseEntity sendHttpRequest(String url, - MultiValueMap headers, - MultiValueMap body) { - // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢? - // 3. 发起请求 - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity responseEntity; - try { - responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); - } catch (RestClientException e) { - log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); - throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); - } - return responseEntity; - } - - protected MultiValueMap buildHttpHeaders(ProcessInstance processInstance, - List headerSettings) { - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); - SimpleModelUtils.addHttpRequestParam(headers, headerSettings, processVariables); - return headers; - } - - protected MultiValueMap buildHttpBody(ProcessInstance processInstance, - List bodySettings) { - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap body = new LinkedMultiValueMap<>(); - SimpleModelUtils.addHttpRequestParam(body, bodySettings, processVariables); - body.add("processInstanceId", processInstance.getId()); - return body; - } - } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index 867af70b9d..fa50cf3917 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -2,13 +2,15 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Component; -import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; /** * BPM HTTP 回调触发器 @@ -19,6 +21,9 @@ import org.springframework.util.MultiValueMap; @Slf4j public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { + @Resource + private RestTemplate restTemplate; + @Resource private BpmProcessInstanceService processInstanceService; @@ -36,16 +41,19 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); return; } - - // 2.1 设置请求头 + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); - // 2.2 设置请求体 - MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 - body.add("taskDefineKey", setting.getCallbackTaskDefineKey()); - - // 3. 发起请求 - sendHttpRequest(setting.getUrl(), headers, body); + setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() + .setKey("taskDefineKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) + .setValue(setting.getCallbackTaskDefineKey())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + false, null, + restTemplate, + processInstanceService); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java index 4a506f3b13..cc377fa1f6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -1,24 +1,15 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; -import com.fasterxml.jackson.core.type.TypeReference; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; -import org.springframework.util.MultiValueMap; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import org.springframework.web.client.RestTemplate; /** * BPM 发送同步 HTTP 请求触发器 @@ -29,6 +20,9 @@ import java.util.Map; @Slf4j public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { + @Resource + private RestTemplate restTemplate; + @Resource private BpmProcessInstanceService processInstanceService; @@ -45,56 +39,15 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); return; } - - // 2.1 设置请求头 + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - MultiValueMap headers = buildHttpHeaders(processInstance, setting.getHeader()); - // 2.2 设置请求体 - MultiValueMap body = buildHttpBody(processInstance, setting.getBody()); - - // 3. 发起请求 - ResponseEntity responseEntity = sendHttpRequest(setting.getUrl(), headers, body); - - // 4.1 判断是否需要解析返回值 - if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) - || !responseEntity.getStatusCode().is2xxSuccessful() - || CollUtil.isEmpty(setting.getResponse())) { - return; - } - // 4.2 解析返回值, 返回值必须符合 CommonResult 规范。 - CommonResult> respResult = JsonUtils.parseObjectQuietly( - responseEntity.getBody(), new TypeReference<>() { - }); - if (respResult == null || !respResult.isSuccess()) { - return; - } - // 4.3 获取需要更新的流程变量 - Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse()); - // 4.4 更新流程变量 - if (CollUtil.isNotEmpty(updateVariables)) { - processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables); - } - } - - /** - * 从请求返回值获取需要更新的流程变量 - * - * @param result 请求返回结果 - * @param responseSettings 返回设置 - * @return 需要更新的流程变量 - */ - private Map getNeedUpdatedVariablesFromResponse(Map result, - List> responseSettings) { - Map updateVariables = new HashMap<>(); - if (CollUtil.isEmpty(result)) { - return updateVariables; - } - responseSettings.forEach(responseSetting -> { - if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { - updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); - } - }); - return updateVariables; + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse(), + restTemplate, + processInstanceService); } } From d3db32b44e8814153586e73354fab739999ca5a5 Mon Sep 17 00:00:00 2001 From: lizhixian <18210040298@163.com> Date: Fri, 14 Mar 2025 16:58:12 +0800 Subject: [PATCH 334/386] =?UTF-8?q?fix=EF=BC=9A=E8=A7=A3=E5=86=B3=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E6=B5=81=E7=A8=8B=E7=94=BB=E5=B8=83=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=EF=BC=8C=E6=9E=81=E7=AB=AF=E6=83=85=E5=86=B5=E4=B8=8B=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E4=BB=BB=E5=8A=A1=E8=8A=82=E7=82=B9=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/service/definition/BpmModelServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index a441803218..33614814d0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -250,6 +250,10 @@ public class BpmModelServiceImpl implements BpmModelService { }); // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); + // 4. 极端情况下无多个用户任务节点,比如发起人-抄送节点 + if (firUserTask == null){ + return; + } Integer candidateStrategy = parseCandidateStrategy(firUserTask); if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName()); From 966357b44e4db079d2db207254148e62463c035c Mon Sep 17 00:00:00 2001 From: puhui999 Date: Fri, 14 Mar 2025 17:34:16 +0800 Subject: [PATCH 335/386] =?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 336/386] =?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 337/386] =?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 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 338/386] =?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 339/386] =?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 4364ef09c533e8f5c336fc48bf5653a88ff18189 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 08:24:57 +0800 Subject: [PATCH 340/386] =?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=E6=B5=81=E7=A8=8B=E5=89=8D?= =?UTF-8?q?=E5=90=8E=E7=BD=AE=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/model/BpmModelMetaInfoVO.java | 2 ++ .../BpmProcessDefinitionInfoDO.java | 7 ++--- .../core/util/BpmHttpRequestUtils.java | 31 ++++++++++--------- .../task/BpmProcessInstanceServiceImpl.java | 3 +- .../task/listener/BpmUserTaskListener.java | 24 +++++--------- .../trigger/http/BpmHttpCallbackTrigger.java | 8 ++--- .../http/BpmSyncHttpRequestTrigger.java | 1 + 7 files changed, 36 insertions(+), 40 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 9fc52fdc0c..254a5178f0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -82,9 +82,11 @@ public class BpmModelMetaInfoVO { @Schema(description = "摘要设置", example = "{}") private SummarySetting summarySetting; + // TODO @lesan:processBeforeTriggerSetting;要不叫这个?主要考虑,notify 留给后续的站内信、短信、邮件这种 notify 通知哈。 @Schema(description = "流程前置通知设置", example = "{}") private HttpRequestSetting PreProcessNotifySetting; + // TODO @lesan:processAfterTriggerSetting @Schema(description = "流程后置通知设置", example = "{}") private HttpRequestSetting PostProcessNotifySetting; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index cc4c697e6f..549889e3aa 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -182,23 +182,22 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.TitleSetting titleSetting; - /** * 摘要设置 */ @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.SummarySetting summarySetting; + // TODO @lesan:processBeforeTriggerSetting;要不叫这个?主要考虑,notify 留给后续的站内信、短信、邮件这种 notify 通知哈。 /** * 流程前置通知设置 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 private BpmModelMetaInfoVO.HttpRequestSetting PreProcessNotifySetting; - /** * 流程后置通知设置 */ - @TableField(typeHandler = JacksonTypeHandler.class) + @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 private BpmModelMetaInfoVO.HttpRequestSetting PostProcessNotifySetting; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java index 74a8da09f5..7f4428aac3 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -37,51 +37,52 @@ public class BpmHttpRequestUtils { public static void executeBpmHttpRequest(ProcessInstance processInstance, String url, - List headerParam, - List bodyParam, + List headerParams, + List bodyParams, Boolean handleResponse, List> response, + // TODO @lesan:RestTemplate 直接通过 springUtil 获取好咧; RestTemplate restTemplate, + // TODO @lesan:processInstanceService 直接通过 springUtil 获取好咧; BpmProcessInstanceService processInstanceService) { // 1.1 设置请求头 - MultiValueMap headers = BpmHttpRequestUtils.buildHttpHeaders(processInstance, headerParam); + MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); // 1.2 设置请求体 - MultiValueMap body = BpmHttpRequestUtils.buildHttpBody(processInstance, bodyParam); + MultiValueMap body = buildHttpBody(processInstance, bodyParams); // 2. 发起请求 - ResponseEntity responseEntity = BpmHttpRequestUtils.sendHttpRequest(url, headers, body, restTemplate); + ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); // 3. 处理返回 + // TODO @lesan:可以用 if return,让括号小点 if (Boolean.TRUE.equals(handleResponse)) { // 3.1 判断是否需要解析返回值 - if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody()) + if (responseEntity == null + || StrUtil.isEmpty(responseEntity.getBody()) || !responseEntity.getStatusCode().is2xxSuccessful() || CollUtil.isEmpty(response)) { return; } // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 - CommonResult> respResult = JsonUtils.parseObjectQuietly( - responseEntity.getBody(), new TypeReference<>() { - }); + CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), + new TypeReference<>() {}); if (respResult == null || !respResult.isSuccess()) { return; } // 3.3 获取需要更新的流程变量 - Map updateVariables = BpmHttpRequestUtils.getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); // 3.4 更新流程变量 if (CollUtil.isNotEmpty(updateVariables)) { processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); } } - } public static ResponseEntity sendHttpRequest(String url, MultiValueMap headers, MultiValueMap body, RestTemplate restTemplate) { - // 3. 发起请求 HttpEntity> requestEntity = new HttpEntity<>(body, headers); ResponseEntity responseEntity; try { @@ -95,7 +96,7 @@ public class BpmHttpRequestUtils { } public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, - List headerSettings) { + List headerSettings) { Map processVariables = processInstance.getProcessVariables(); MultiValueMap headers = new LinkedMultiValueMap<>(); headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); @@ -104,7 +105,7 @@ public class BpmHttpRequestUtils { } public static MultiValueMap buildHttpBody(ProcessInstance processInstance, - List bodySettings) { + List bodySettings) { Map processVariables = processInstance.getProcessVariables(); MultiValueMap body = new LinkedMultiValueMap<>(); addHttpRequestParam(body, bodySettings, processVariables); @@ -120,7 +121,7 @@ public class BpmHttpRequestUtils { * @return 需要更新的流程变量 */ public static Map getNeedUpdatedVariablesFromResponse(Map result, - List> responseSettings) { + List> responseSettings) { Map updateVariables = new HashMap<>(); if (CollUtil.isEmpty(result)) { return updateVariables; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 4ca5a189ff..64abcef42e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -935,10 +935,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 流程前置通知 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. getProcessDefinitionInfo(instance.getProcessDefinitionId()); + // TODO @lesan:if return 哈。减少括号。 if (ObjUtil.isNotNull(processDefinitionInfo) && ObjUtil.isNotNull(processDefinitionInfo.getPreProcessNotifySetting())) { BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPreProcessNotifySetting(); - BpmHttpRequestUtils.executeBpmHttpRequest(instance, setting.getUrl(), setting.getHeader(), @@ -949,4 +949,5 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } }); } + } 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 d02844353d..6048423c91 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 @@ -46,22 +46,14 @@ public class BpmUserTaskListener implements TaskListener { // 2. 发起请求 // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getProcessInstanceId())); - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getAssignee())); - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getTaskDefinitionKey())); - listenerHandler.getBody() - .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(delegateTask.getId())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getAssignee())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getTaskDefinitionKey())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId())); BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, listenerHandler.getPath(), listenerHandler.getHeader(), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index fa50cf3917..ab1f54abd4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -41,13 +41,12 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); return; } + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() - .setKey("taskDefineKey") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()) - .setValue(setting.getCallbackTaskDefineKey())); + .setKey("taskDefineKey") // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey())); BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, setting.getUrl(), setting.getHeader(), @@ -56,4 +55,5 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { restTemplate, processInstanceService); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java index cc377fa1f6..6c74b066f0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -39,6 +39,7 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); return; } + // 2. 发起请求 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, From 25899d99885c68e40335e748f4b178e8ab613501 Mon Sep 17 00:00:00 2001 From: LesanOuO <1960681385@qq.com> Date: Sat, 15 Mar 2025 10:45:45 +0800 Subject: [PATCH 341/386] =?UTF-8?q?fix:=20=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vo/model/BpmModelMetaInfoVO.java | 6 +-- .../BpmProcessDefinitionInfoDO.java | 5 +- .../core/util/BpmHttpRequestUtils.java | 51 +++++++++---------- .../task/BpmProcessInstanceServiceImpl.java | 31 +++++------ .../task/listener/BpmUserTaskListener.java | 7 +-- .../trigger/http/BpmHttpCallbackTrigger.java | 7 +-- .../http/BpmSyncHttpRequestTrigger.java | 7 +-- 7 files changed, 44 insertions(+), 70 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 254a5178f0..cf9ca3e5fd 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -82,13 +82,11 @@ public class BpmModelMetaInfoVO { @Schema(description = "摘要设置", example = "{}") private SummarySetting summarySetting; - // TODO @lesan:processBeforeTriggerSetting;要不叫这个?主要考虑,notify 留给后续的站内信、短信、邮件这种 notify 通知哈。 @Schema(description = "流程前置通知设置", example = "{}") - private HttpRequestSetting PreProcessNotifySetting; + private HttpRequestSetting processBeforeTriggerSetting; - // TODO @lesan:processAfterTriggerSetting @Schema(description = "流程后置通知设置", example = "{}") - private HttpRequestSetting PostProcessNotifySetting; + private HttpRequestSetting processAfterTriggerSetting; @Schema(description = "流程 ID 规则") @Data diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index 549889e3aa..6a4c333ab2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -188,16 +188,15 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.SummarySetting summarySetting; - // TODO @lesan:processBeforeTriggerSetting;要不叫这个?主要考虑,notify 留给后续的站内信、短信、邮件这种 notify 通知哈。 /** * 流程前置通知设置 */ @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 - private BpmModelMetaInfoVO.HttpRequestSetting PreProcessNotifySetting; + private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting; /** * 流程后置通知设置 */ @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 - private BpmModelMetaInfoVO.HttpRequestSetting PostProcessNotifySetting; + private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java index 7f4428aac3..2503c0fff9 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; @@ -40,11 +41,9 @@ public class BpmHttpRequestUtils { List headerParams, List bodyParams, Boolean handleResponse, - List> response, - // TODO @lesan:RestTemplate 直接通过 springUtil 获取好咧; - RestTemplate restTemplate, - // TODO @lesan:processInstanceService 直接通过 springUtil 获取好咧; - BpmProcessInstanceService processInstanceService) { + List> response) { + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); + BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); // 1.1 设置请求头 MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); @@ -55,27 +54,27 @@ public class BpmHttpRequestUtils { ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); // 3. 处理返回 - // TODO @lesan:可以用 if return,让括号小点 - if (Boolean.TRUE.equals(handleResponse)) { - // 3.1 判断是否需要解析返回值 - if (responseEntity == null - || StrUtil.isEmpty(responseEntity.getBody()) - || !responseEntity.getStatusCode().is2xxSuccessful() - || CollUtil.isEmpty(response)) { - return; - } - // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 - CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), - new TypeReference<>() {}); - if (respResult == null || !respResult.isSuccess()) { - return; - } - // 3.3 获取需要更新的流程变量 - Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); - // 3.4 更新流程变量 - if (CollUtil.isNotEmpty(updateVariables)) { - processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); - } + if (Boolean.FALSE.equals(handleResponse)) { + return; + } + // 3.1 判断是否需要解析返回值 + if (responseEntity == null + || StrUtil.isEmpty(responseEntity.getBody()) + || !responseEntity.getStatusCode().is2xxSuccessful() + || CollUtil.isEmpty(response)) { + return; + } + // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 + CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), + new TypeReference<>() {}); + if (respResult == null || !respResult.isSuccess()) { + return; + } + // 3.3 获取需要更新的流程变量 + Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + // 3.4 更新流程变量 + if (CollUtil.isNotEmpty(updateVariables)) { + processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 64abcef42e..58417d922b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -122,9 +122,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @Resource private BpmProcessIdRedisDAO processIdRedisDAO; - @Resource - private RestTemplate restTemplate; - // ========== Query 查询相关方法 ========== @Override @@ -913,16 +910,14 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. getProcessDefinitionInfo(instance.getProcessDefinitionId()); if (ObjUtil.isNotNull(processDefinitionInfo) && - ObjUtil.isNotNull(processDefinitionInfo.getPostProcessNotifySetting())) { - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPostProcessNotifySetting(); + ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); BpmHttpRequestUtils.executeBpmHttpRequest(instance, setting.getUrl(), setting.getHeader(), setting.getBody(), - true, setting.getResponse(), - restTemplate, - this); + true, setting.getResponse()); } } }); @@ -935,18 +930,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 流程前置通知 BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. getProcessDefinitionInfo(instance.getProcessDefinitionId()); - // TODO @lesan:if return 哈。减少括号。 - if (ObjUtil.isNotNull(processDefinitionInfo) && - ObjUtil.isNotNull(processDefinitionInfo.getPreProcessNotifySetting())) { - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPreProcessNotifySetting(); - BpmHttpRequestUtils.executeBpmHttpRequest(instance, - setting.getUrl(), - setting.getHeader(), - setting.getBody(), - true, setting.getResponse(), - restTemplate, - this); + if (ObjUtil.isNull(processDefinitionInfo) || + ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) { + return; } + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), + setting.getHeader(), + setting.getBody(), + true, setting.getResponse()); }); } 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 6048423c91..e16fafa2d1 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 @@ -32,9 +32,6 @@ public class BpmUserTaskListener implements TaskListener { @Resource private BpmProcessInstanceService processInstanceService; - @Resource - private RestTemplate restTemplate; - @Setter private FixedValue listenerConfig; @@ -58,9 +55,7 @@ public class BpmUserTaskListener implements TaskListener { listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), - false, null, - restTemplate, - processInstanceService); + false, null); // 3. 是否需要后续操作?TODO 芋艿:待定! } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java index ab1f54abd4..351b57ddd4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -21,9 +21,6 @@ import org.springframework.web.client.RestTemplate; @Slf4j public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { - @Resource - private RestTemplate restTemplate; - @Resource private BpmProcessInstanceService processInstanceService; @@ -51,9 +48,7 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { setting.getUrl(), setting.getHeader(), setting.getBody(), - false, null, - restTemplate, - processInstanceService); + false, null); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java index 6c74b066f0..2ac04117e4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -20,9 +20,6 @@ import org.springframework.web.client.RestTemplate; @Slf4j public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { - @Resource - private RestTemplate restTemplate; - @Resource private BpmProcessInstanceService processInstanceService; @@ -46,9 +43,7 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { setting.getUrl(), setting.getHeader(), setting.getBody(), - true, setting.getResponse(), - restTemplate, - processInstanceService); + true, setting.getResponse()); } } From 045fb584d25b0109c31e8822c6f567d4fa6756b3 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 11:21:29 +0800 Subject: [PATCH 342/386] =?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=E5=AD=90=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E6=83=B3=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/task/BpmProcessInstanceController.java | 1 + .../flowable/core/candidate/BpmTaskCandidateInvoker.java | 7 ++++++- .../bpm/service/task/BpmProcessInstanceServiceImpl.java | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 8a3777772e..9316261678 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -148,6 +148,7 @@ public class BpmProcessInstanceController { processDefinition, processDefinitionInfo, startUser, dept)); } + // TODO @lesan:【子流程】子流程如果取消,主流程应该是通过、还是不通过哈?还是禁用掉子流程的取消? @DeleteMapping("/cancel-by-start-user") @Operation(summary = "用户取消流程实例", description = "取消发起的流程") @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java index 952f0f1bea..7f66b29d3b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java @@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.CallActivity; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; @@ -129,8 +130,12 @@ public class BpmTaskCandidateInvoker { public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, Long startUserId, String processDefinitionId, Map processVariables) { - // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 + // 如果是 CallActivity 子流程,不进行计算候选人 FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + if (flowElement instanceof CallActivity) { + return new HashSet<>(); + } + // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 Integer approveType = BpmnModelUtils.parseApproveType(flowElement); if (ObjectUtils.equalsAny(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 64abcef42e..1bd568c8e5 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -390,9 +390,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, List activities, List tasks) { // 遍历 tasks 列表,只处理已结束的 UserTask - // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities - // 的话,它无法成为一个节点 - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会导致timeline不会展示已结束的子流程 + // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程; + // TODO @lesan:【子流程】基于 activities 查询出 usertask、callactivity,然后拼接?如果是子流程,就是可以点击过去? List endTasks = filterList(tasks, task -> task.getEndTime() != null); List approvalNodes = convertList(endTasks, task -> { FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -468,6 +468,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService HistoricActivityInstance::getActivityId); // 按照 activityId 分组,构建 ApprovalNodeInfo 节点 + // TODO @lesan:【子流程】在子流程进行审批的时候,HistoricActivityInstance 里面可以拿到 runActivities.get(0).getCalledProcessInstanceId()。要不要支持跳转??? Map taskMap = convertMap(tasks, HistoricTaskInstance::getId); return convertList(runningTaskMap.entrySet(), entry -> { String activityId = entry.getKey(); From 71216a50bf933d92846361851634ce2540ec2297 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 13:15:36 +0800 Subject: [PATCH 343/386] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20README=EF=BC=9A?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=88=E5=A2=9E=E5=8A=A0=E9=92=89?= =?UTF-8?q?=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6=EF=BC=89=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 935c0caad8..580fabbf76 100644 --- a/README.md +++ b/README.md @@ -149,22 +149,45 @@ ### 工作流程 -| | 功能 | 描述 | -|----|-------|-----------------------------------------| -| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 | -| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 | -| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 | -| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 | -| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 | -| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 | -| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 | +![BPM 功能列表](/img/common/bpm-feature.png) -![功能图](/.image/common/bpm-feature.png) +基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作: | BPMN 设计器 | 钉钉/飞书设计器 | |------------------------------|--------------------------------| | ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) | +> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!! +> +> 前者支持轻量配置简单流程,后者实现复杂场景深度编排 + +| 功能列表 | 功能描述 | 是否完成 | +|------------|-------------------------------------------------------------------------------------|------| +| SIMPLE 设计器 | 仿钉钉/飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置 | ✅ | +| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 | ✅ | +| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 | ✅ | +| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 | ✅ | +| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅ | +| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 | ✅ | +| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 | ✅ | +| 转办 | A 转给其 B 审批,B 审批后,进入下一节点 | ✅ | +| 委派 | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点 | ✅ | +| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 | ✅ | +| 减签 | (取消加签)在当前审批人操作之前,减少审批人 | ✅ | +| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 | ✅ | +| 终止 | 系统管理员,在任意节点终止流程实例 | ✅ | +| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 | ✅ | +| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 | ✅ | +| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 | ✅ | +| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 | ✅ | +| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 | ✅ | +| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 | ✅ | +| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 | ✅ | +| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) | ✅ | +| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 | ✅ | +| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 | ✅ | +| 拓展设置 | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 | ✅ | + ### 支付系统 | | 功能 | 描述 | From a193322373d5b833b028501f96a69d3ec76330f4 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 13:16:31 +0800 Subject: [PATCH 344/386] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20README=EF=BC=9A?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=88=E5=A2=9E=E5=8A=A0=E9=92=89?= =?UTF-8?q?=E9=92=89=E3=80=81=E9=A3=9E=E4=B9=A6=EF=BC=89=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 580fabbf76..f326bcadf4 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ ### 工作流程 -![BPM 功能列表](/img/common/bpm-feature.png) +![功能图](/.image/common/bpm-feature.png) 基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作: From 0ab54a9fe42ddc72af31df119d5665fac3f6e2d8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Mar 2025 16:48:05 +0800 Subject: [PATCH 345/386] =?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=9Afix:=20=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E8=8A=82=E7=82=B9=E8=A1=A8=E5=8D=95=E6=97=A0?= =?UTF-8?q?=E5=8F=AF=E7=BC=96=E8=BE=91=E5=AD=97=E6=AE=B5=E6=97=B6=EF=BC=8C?= =?UTF-8?q?variables=E6=B5=81=E7=A8=8B=E5=8F=98=E9=87=8F=E5=80=BC=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=EF=BC=8C=E6=B5=81=E7=A8=8B=E8=8A=82=E7=82=B9=E6=B5=81?= =?UTF-8?q?=E8=BD=AC=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BpmProcessDefinitionInfoDO.java | 4 ++-- .../flowable/core/util/BpmnModelUtils.java | 6 ----- .../definition/BpmModelServiceImpl.java | 3 +-- .../bpm/service/task/BpmTaskServiceImpl.java | 22 +++++++++---------- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index 6a4c333ab2..86c83ed611 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -191,12 +191,12 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { /** * 流程前置通知设置 */ - @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 + @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting; /** * 流程后置通知设置 */ - @TableField(typeHandler = JacksonTypeHandler.class, exist = false) // TODO @芋艿:临时注释 exist,因为要合并 master-jdk17 + @TableField(typeHandler = JacksonTypeHandler.class) private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index fdfb724442..1cccf18f04 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -21,7 +21,6 @@ import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; import org.flowable.common.engine.api.FlowableException; -import org.flowable.common.engine.impl.javax.el.PropertyNotFoundException; import org.flowable.common.engine.impl.util.io.BytesStreamSource; import org.flowable.engine.impl.el.FixedValue; @@ -1007,11 +1006,6 @@ public class BpmnModelUtils { Object result = FlowableUtils.getExpressionValue(variables, expression); return Boolean.TRUE.equals(result); } catch (FlowableException ex) { - // TODO @芋艿 临时方案解决流程变量中不包含条件表达式时报错问题,如果expression 的计算,可能不依赖于 variables,getExpressionValue方法应该需要重构 - if (ex.getCause() instanceof PropertyNotFoundException){ - log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); - return Boolean.FALSE; - } // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); return Boolean.FALSE; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java index 33614814d0..e8e90006f8 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java @@ -250,8 +250,7 @@ public class BpmModelServiceImpl implements BpmModelService { }); // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); - // 4. 极端情况下无多个用户任务节点,比如发起人-抄送节点 - if (firUserTask == null){ + if (firUserTask == null) { return; } Integer candidateStrategy = parseCandidateStrategy(firUserTask); 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 b15a59c540..67a0ddf7de 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 @@ -557,31 +557,31 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - // 如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时variables一定会为空 - // 场景一:A节点发起,B节点表单无可编辑字段,审批通过时,C节点需要流程变量获取下一个执行节点,但因为B节点无可编辑的字段,variables为空,流程可能出现问题 - // 场景二:A节点发起,B节点只有某一个字段可编辑(比如day),但C节点需要多个节点(比如workday,在发起时填写,因为B节点只有day的编辑权限,在审批后。variables会缺少work的值) - // 3.1 设置流程变量 + + // 3. 设置流程变量。如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables 一定会为空 + // 场景一:A 节点发起,B 节点表单无可编辑字段,审批通过时,C 节点需要流程变量获取下一个执行节点,但因为 B 节点无可编辑的字段,variables 为空,流程可能出现问题。 + // 场景二:A 节点发起,B 节点只有某一个字段可编辑(比如 day),但 C 节点需要多个节点。 + // (比如 work + day 变量,在发起时填写,因为 B 节点只有 day 的编辑权限,在审批后,variables 会缺少 work 的值) Map processVariables = new HashMap<>(); - // 3.2 获取历史中流程变量 - if (CollUtil.isNotEmpty(instance.getProcessVariables())) { + if (CollUtil.isNotEmpty(instance.getProcessVariables())) { // 获取历史中流程变量 processVariables.putAll(instance.getProcessVariables()); } - // 3.3 合并前端传递的流程变量,以前端为准 - if (CollUtil.isNotEmpty(reqVO.getVariables())) { + if (CollUtil.isNotEmpty(reqVO.getVariables())) { // 合并前端传递的流程变量,以前端为准 processVariables.putAll(reqVO.getVariables()); } - // 3.4 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + + // 4. 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, bpmnModel, reqVO.getNextAssignees(), instance); runtimeService.setVariables(task.getProcessInstanceId(), variables); - // 4 调用 BPM complete 去完成任务 + + // 5. 调用 BPM complete 去完成任务 taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 handleParentTaskIfSign(task.getParentTaskId()); } - /** * 校验选择的下一个节点的审批人,是否合法 * 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 346/386] =?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 347/386] =?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 348/386] =?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 349/386] =?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 350/386] =?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 351/386] =?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 352/386] =?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 353/386] =?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 354/386] =?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 75167383304c9f5c392af8553bc7e1e70736d043 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 13:52:33 +0800 Subject: [PATCH 355/386] =?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=E4=BF=AE=E5=A4=8D=20task=20?= =?UTF-8?q?=E7=9A=84=20category=20=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/behavior/BpmUserTaskActivityBehavior.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java index cba5187b38..c4c8167c87 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java @@ -10,7 +10,10 @@ import org.flowable.common.engine.impl.el.ExpressionManager; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.impl.util.TaskHelper; +import org.flowable.engine.interceptor.CreateUserTaskBeforeContext; import org.flowable.task.service.TaskService; import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.springframework.transaction.annotation.Transactional; @@ -69,4 +72,15 @@ public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { return CollUtil.get(candidateUserIds, index); } + @Override + protected void handleCategory(CreateUserTaskBeforeContext beforeContext, ExpressionManager expressionManager, + TaskEntity task, DelegateExecution execution) { + ProcessDefinitionEntity processDefinitionEntity = CommandContextUtil.getProcessDefinitionEntityManager().findById(execution.getProcessDefinitionId()); + if (processDefinitionEntity == null) { + log.warn("[handleCategory][任务编号({}) 找不到流程定义({})]", task.getId(), execution.getProcessDefinitionId()); + return; + } + task.setCategory(processDefinitionEntity.getCategory()); + } + } From fc8e4662bb6998df397950e6b7318e164b8d23f9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 14:45:09 +0800 Subject: [PATCH 356/386] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E3=80=91=E9=99=90=E6=B5=81=E7=9A=84=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=20expire=20=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=EF=BC=8C=E5=8E=9F=E5=9B=A0=E5=8F=82=E8=A7=81=20https://t.zsxq.?= =?UTF-8?q?com/lcR0W=20=E5=9C=BA=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/ratelimiter/core/redis/RateLimiterRedisDAO.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java index fc1378f3bd..18c30682e5 100644 --- a/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java +++ b/yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/ratelimiter/core/redis/RateLimiterRedisDAO.java @@ -44,6 +44,7 @@ public class RateLimiterRedisDAO { RateLimiterConfig config = rateLimiter.getConfig(); if (config == null) { rateLimiter.trySetRate(RateType.OVERALL, count, rateInterval, RateIntervalUnit.SECONDS); + rateLimiter.expire(rateInterval, TimeUnit.SECONDS); // 原因参见 https://t.zsxq.com/lcR0W return rateLimiter; } // 2. 如果存在,并且配置相同,则直接返回 @@ -54,6 +55,7 @@ public class RateLimiterRedisDAO { } // 3. 如果存在,并且配置不同,则进行新建 rateLimiter.setRate(RateType.OVERALL, count, rateInterval, RateIntervalUnit.SECONDS); + rateLimiter.expire(rateInterval, TimeUnit.SECONDS); // 原因参见 https://t.zsxq.com/lcR0W return rateLimiter; } From 2ddf9d05e6d1cda118ebd66a497d94ae7c915e64 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 15:24:56 +0800 Subject: [PATCH 357/386] =?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=E4=BD=BF=E7=94=A8=20DataPermissi?= =?UTF-8?q?onUtils=20=E6=9B=BF=E4=BB=A3=20DataPermission=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=20this=20=E8=B0=83=E7=94=A8=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E7=94=9F=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/system/api/user/AdminUserApiImpl.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java index 6af7bcd422..4f7b4e4682 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.system.api.user; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; @@ -12,7 +12,10 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; @@ -56,10 +59,11 @@ public class AdminUserApiImpl implements AdminUserApi { } @Override - @DataPermission(enable = false) // 禁用数据权限。原因是,一般基于指定 id 的 API 查询,都是数据拼接为主 public List getUserList(Collection ids) { - List users = userService.getUserList(ids); - return BeanUtils.toBean(users, AdminUserRespDTO.class); + return DataPermissionUtils.executeIgnore(() -> { // 禁用数据权限。原因是,一般基于指定 id 的 API 查询,都是数据拼接为主 + List users = userService.getUserList(ids); + return BeanUtils.toBean(users, AdminUserRespDTO.class); + }); } @Override From dc1c824749e6f714843fbe22053a13480183190b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 15:29:50 +0800 Subject: [PATCH 358/386] =?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=9AprocessTaskAssigned=20=E5=BF=BD?= =?UTF-8?q?=E7=95=A5=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E5=88=86=E9=85=8D=E7=BB=99=20leader=20=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E5=80=99=EF=BC=8C=E5=9B=A0=E4=B8=BA=E6=B2=A1=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=9D=83=E9=99=90=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E4=B8=8D=E5=88=B0?= 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, 2 insertions(+) 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 67a0ddf7de..242ed77475 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 @@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; 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.object.PageUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; @@ -1255,6 +1256,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } @Override + @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 public void processTaskAssigned(Task task) { // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { From 2dc8071faa763f835c9b67e87bf570af38bd000f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 16 Mar 2025 16:31:36 +0800 Subject: [PATCH 359/386] =?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 360/386] =?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 361/386] =?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 362/386] =?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 363/386] =?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 364/386] =?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 365/386] =?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 366/386] =?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 367/386] =?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 368/386] =?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 369/386] =?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 370/386] =?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 371/386] =?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 372/386] =?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 373/386] =?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 374/386] =?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 7b3401e216265f099d860f4d1658376e387b2c91 Mon Sep 17 00:00:00 2001 From: zzt <976209226@qq.com> Date: Tue, 18 Mar 2025 00:22:15 +0800 Subject: [PATCH 375/386] =?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=94=BB=E5=9B=BE=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E5=8A=9F=E8=83=BD=E5=A2=9E=E5=8A=A0=E7=A1=85=E5=9F=BA?= =?UTF-8?q?=E6=B5=81=E5=8A=A8=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/service/image/AiImageServiceImpl.java | 8 +- .../ai/config/YudaoAiAutoConfiguration.java | 6 +- .../ai/core/factory/AiModelFactoryImpl.java | 14 ++ .../siliconflow/SiiconflowApiConstants.java | 36 +++ .../model/siliconflow/SiiconflowmageApi.java | 207 ++++++++++++++++++ .../siliconflow/SiliconFlowChatModel.java | 4 - .../siliconflow/SiliconflowImageModel.java | 166 ++++++++++++++ .../siliconflow/SiliconflowImageOptions.java | 166 ++++++++++++++ .../yudao/framework/ai/core/util/AiUtils.java | 4 +- .../ai/chat/SiliconFlowChatModelTests.java | 5 +- 10 files changed, 605 insertions(+), 11 deletions(-) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java 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 60ca9ac996..f0ef418301 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 @@ -11,6 +11,7 @@ import cn.hutool.extra.spring.SpringUtil; import cn.hutool.http.HttpUtil; 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.siliconflow.SiliconflowImageOptions; 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.AiImageDrawReqVO; @@ -144,7 +145,12 @@ public class AiImageServiceImpl implements AiImageService { .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 .withResponseFormat("b64_json") .build(); - } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { + } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) { + // https://docs.siliconflow.cn/cn/api-reference/images/images-generations + return SiliconflowImageOptions.builder().withModel(model.getModel()) + .withHeight(draw.getHeight()).withWidth(draw.getWidth()) + .build(); + } 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(model.getModel()) 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 ef3314a48b..dc1846ef99 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 @@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi; 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; @@ -113,11 +115,11 @@ public class YudaoAiAutoConfiguration { public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) { if (StrUtil.isEmpty(properties.getModel())) { - properties.setModel(SiliconFlowChatModel.MODEL_DEFAULT); + properties.setModel(SiiconflowApiConstants.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() - .baseUrl(SiliconFlowChatModel.BASE_URL) + .baseUrl(SiiconflowApiConstants.DEFAULT_BASE_URL) .apiKey(properties.getApiKey()) .build()) .defaultOptions(OpenAiChatOptions.builder() 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 356715be26..6c72b2b5f3 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 @@ -15,7 +15,10 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi; import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageModel; 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; @@ -224,6 +227,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildZhiPuAiImageModel(apiKey, url); case OPENAI: return buildOpenAiImageModel(apiKey, url); + case SILICON_FLOW: + return buildSiiconflowImageModel(apiKey,url); case STABLE_DIFFUSION: return buildStabilityAiImageModel(apiKey, url); default: @@ -468,6 +473,15 @@ public class AiModelFactoryImpl implements AiModelFactory { return new OpenAiImageModel(openAiApi); } + /** + * Siiconflow + */ + private SiliconflowImageModel buildSiiconflowImageModel(String apiToken, String url) { + url = StrUtil.blankToDefault(url, SiiconflowApiConstants.DEFAULT_BASE_URL); + SiiconflowmageApi openAiApi = SiiconflowmageApi.builder().baseUrl(url).apiKey(apiToken).build(); + return new SiliconflowImageModel(openAiApi); + } + /** * 可参考 {@link OllamaAutoConfiguration} 的 ollamaApi 方法 */ diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java new file mode 100644 index 0000000000..589d05855d --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java @@ -0,0 +1,36 @@ +/* + * 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 cn.iocoder.yudao.framework.ai.core.model.siliconflow; + +/** + * Common value constants for Siiconflow api. + * + * @author zzt + */ +public final class SiiconflowApiConstants { + + public static final String DEFAULT_BASE_URL = "https://api.siliconflow.cn"; + + public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"; + + public static final String PROVIDER_NAME = "Siiconflow"; + + private SiiconflowApiConstants() { + + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java new file mode 100644 index 0000000000..8d0cf925af --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java @@ -0,0 +1,207 @@ +/* + * 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 cn.iocoder.yudao.framework.ai.core.model.siliconflow; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.ai.model.ApiKey; +import org.springframework.ai.model.NoopApiKey; +import org.springframework.ai.model.SimpleApiKey; +import org.springframework.ai.openai.api.OpenAiImageApi; +import org.springframework.ai.retry.RetryUtils; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestClient; + +import java.util.Map; + +/** + * Siiconflow Image API. + * + * @see Images + * + * @author zzt + */ +public class SiiconflowmageApi { + + private final RestClient restClient; + + /** + * Create a new Siiconflow Image api with base URL set. + * @param aiToken OpenAI apiKey. + */ + public SiiconflowmageApi(String aiToken) { + this(SiiconflowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder()); + } + + /** + * Create a new Siiconflow Image API with the provided base URL. + * @param baseUrl the base URL for the OpenAI API. + * @param openAiToken Siiconflow apiKey. + */ + public SiiconflowmageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) { + this(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER); + } + + /** + * Create a new OpenAI Image API with the provided base URL. + * @param baseUrl the base URL for the OpenAI API. + * @param apiKey OpenAI apiKey. + * @param restClientBuilder the rest client builder to use. + */ + public SiiconflowmageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder, + ResponseErrorHandler responseErrorHandler) { + this(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler); + } + + /** + * Create a new OpenAI Image API with the provided base URL. + * @param baseUrl the base URL for the OpenAI API. + * @param apiKey OpenAI apiKey. + * @param headers the http headers to use. + * @param restClientBuilder the rest client builder to use. + * @param responseErrorHandler the response error handler to use. + */ + public SiiconflowmageApi(String baseUrl, String apiKey, MultiValueMap headers, + RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { + + this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler); + } + + /** + * Create a new OpenAI Image API with the provided base URL. + * @param baseUrl the base URL for the OpenAI API. + * @param apiKey OpenAI apiKey. + * @param headers the http headers to use. + * @param restClientBuilder the rest client builder to use. + * @param responseErrorHandler the response error handler to use. + */ + public SiiconflowmageApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, + RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { + + // @formatter:off + this.restClient = restClientBuilder.baseUrl(baseUrl) + .defaultHeaders(h -> { + if(!(apiKey instanceof NoopApiKey)) { + h.setBearerAuth(apiKey.getValue()); + } + h.setContentType(MediaType.APPLICATION_JSON); + h.addAll(headers); + }) + .defaultStatusHandler(responseErrorHandler) + .build(); + // @formatter:on + } + + public ResponseEntity createImage(SiliconflowImageRequest siliconflowImageRequest) { + Assert.notNull(siliconflowImageRequest, "Image request cannot be null."); + Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty."); + + return this.restClient.post() + .uri("v1/images/generations") + .body(siliconflowImageRequest) + .retrieve() + .toEntity(OpenAiImageApi.OpenAiImageResponse.class); + } + + + // @formatter:off + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SiliconflowImageRequest ( + @JsonProperty("prompt") String prompt, + @JsonProperty("model") String model, + @JsonProperty("batch_size") Integer batchSize, + @JsonProperty("negative_prompt") String negativePrompt, + @JsonProperty("seed") Integer seed, + @JsonProperty("num_inference_steps") Integer numInferenceSteps, + @JsonProperty("guidance_scale") Float guidanceScale, + @JsonProperty("image") String image) { + + public SiliconflowImageRequest(String prompt, String model) { + this(prompt, model, null, null, null, null, null, null); + } + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder to construct {@link SiiconflowmageApi} instance. + */ + public static class Builder { + + private String baseUrl = SiiconflowApiConstants.DEFAULT_BASE_URL; + + private ApiKey apiKey; + + private MultiValueMap headers = new LinkedMultiValueMap<>(); + + private RestClient.Builder restClientBuilder = RestClient.builder(); + + private ResponseErrorHandler responseErrorHandler = RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER; + + public Builder baseUrl(String baseUrl) { + Assert.hasText(baseUrl, "baseUrl cannot be null or empty"); + this.baseUrl = baseUrl; + return this; + } + + public Builder apiKey(ApiKey apiKey) { + Assert.notNull(apiKey, "apiKey cannot be null"); + this.apiKey = apiKey; + return this; + } + + public Builder apiKey(String simpleApiKey) { + Assert.notNull(simpleApiKey, "simpleApiKey cannot be null"); + this.apiKey = new SimpleApiKey(simpleApiKey); + return this; + } + + public Builder headers(MultiValueMap headers) { + Assert.notNull(headers, "headers cannot be null"); + this.headers = headers; + return this; + } + + public Builder restClientBuilder(RestClient.Builder restClientBuilder) { + Assert.notNull(restClientBuilder, "restClientBuilder cannot be null"); + this.restClientBuilder = restClientBuilder; + return this; + } + + public Builder responseErrorHandler(ResponseErrorHandler responseErrorHandler) { + Assert.notNull(responseErrorHandler, "responseErrorHandler cannot be null"); + this.responseErrorHandler = responseErrorHandler; + return this; + } + + public SiiconflowmageApi build() { + Assert.notNull(this.apiKey, "apiKey must be set"); + return new SiiconflowmageApi(this.baseUrl, this.apiKey, this.headers, this.restClientBuilder, + this.responseErrorHandler); + } + + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java index cada37987d..cda2cb378a 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java @@ -20,10 +20,6 @@ import reactor.core.publisher.Flux; @RequiredArgsConstructor public class SiliconFlowChatModel implements ChatModel { - public static final String BASE_URL = "https://api.siliconflow.cn"; - - public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"; - /** * 兼容 OpenAI 接口,进行复用 */ diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java new file mode 100644 index 0000000000..0e137da726 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java @@ -0,0 +1,166 @@ +/* + * 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 cn.iocoder.yudao.framework.ai.core.model.siliconflow; + +import io.micrometer.observation.ObservationRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.image.*; +import org.springframework.ai.image.observation.DefaultImageModelObservationConvention; +import org.springframework.ai.image.observation.ImageModelObservationContext; +import org.springframework.ai.image.observation.ImageModelObservationConvention; +import org.springframework.ai.image.observation.ImageModelObservationDocumentation; +import org.springframework.ai.model.ModelOptionsUtils; +import org.springframework.ai.openai.api.OpenAiImageApi; +import org.springframework.ai.openai.api.common.OpenAiApiConstants; +import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata; +import org.springframework.ai.retry.RetryUtils; +import org.springframework.http.ResponseEntity; +import org.springframework.retry.support.RetryTemplate; +import org.springframework.util.Assert; + +import java.util.List; + +/** + * cv openapi图片模型方法 + * + * @author zzt + */ +public class SiliconflowImageModel implements ImageModel { + + private static final Logger logger = LoggerFactory.getLogger(SiliconflowImageModel.class); + + private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention(); + + /** + * The default options used for the image completion requests. + */ + private final SiliconflowImageOptions defaultOptions; + + /** + * The retry template used to retry the OpenAI Image API calls. + */ + private final RetryTemplate retryTemplate; + + /** + * Low-level access to the OpenAI Image API. + */ + private final SiiconflowmageApi siiconflowmageApi; + + /** + * Observation registry used for instrumentation. + */ + private final ObservationRegistry observationRegistry; + + /** + * Conventions to use for generating observations. + */ + private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION; + + /** + * Creates an instance of the OpenAiImageModel. + * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with + * the OpenAI Image API. + * @throws IllegalArgumentException if openAiImageApi is null + */ + public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi) { + this(siiconflowmageApi, SiliconflowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); + } + + /** + * Initializes a new instance of the OpenAiImageModel. + * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with + * the OpenAI Image API. + * @param options The OpenAiImageOptions to configure the image model. + * @param retryTemplate The retry template. + */ + public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi, SiliconflowImageOptions options, RetryTemplate retryTemplate) { + this(siiconflowmageApi, options, retryTemplate, ObservationRegistry.NOOP); + } + + /** + * Initializes a new instance of the OpenAiImageModel. + * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with + * the OpenAI Image API. + * @param options The OpenAiImageOptions to configure the image model. + * @param retryTemplate The retry template. + * @param observationRegistry The ObservationRegistry used for instrumentation. + */ + public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi, SiliconflowImageOptions options, RetryTemplate retryTemplate, + ObservationRegistry observationRegistry) { + Assert.notNull(siiconflowmageApi, "OpenAiImageApi must not be null"); + Assert.notNull(options, "options must not be null"); + Assert.notNull(retryTemplate, "retryTemplate must not be null"); + Assert.notNull(observationRegistry, "observationRegistry must not be null"); + this.siiconflowmageApi = siiconflowmageApi; + this.defaultOptions = options; + this.retryTemplate = retryTemplate; + this.observationRegistry = observationRegistry; + } + + @Override + public ImageResponse call(ImagePrompt imagePrompt) { + SiiconflowmageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt); + + var observationContext = ImageModelObservationContext.builder() + .imagePrompt(imagePrompt) + .provider(OpenAiApiConstants.PROVIDER_NAME) + .requestOptions(imagePrompt.getOptions()) + .build(); + + return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION + .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext, + this.observationRegistry) + .observe(() -> { + ResponseEntity imageResponseEntity = this.retryTemplate + .execute(ctx -> this.siiconflowmageApi.createImage(imageRequest)); + + ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest); + + observationContext.setResponse(imageResponse); + + return imageResponse; + }); + } + + private SiiconflowmageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt) { + String instructions = imagePrompt.getInstructions().get(0).getText(); + + SiiconflowmageApi.SiliconflowImageRequest imageRequest = new SiiconflowmageApi.SiliconflowImageRequest(instructions, + imagePrompt.getOptions().getModel()); + + return ModelOptionsUtils.merge(imagePrompt.getOptions(), imageRequest, SiiconflowmageApi.SiliconflowImageRequest.class); + } + + private ImageResponse convertResponse(ResponseEntity imageResponseEntity, + SiiconflowmageApi.SiliconflowImageRequest siliconflowImageRequest) { + OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody(); + if (imageApiResponse == null) { + logger.warn("No image response returned for request: {}", siliconflowImageRequest); + return new ImageResponse(List.of()); + } + + List imageGenerationList = imageApiResponse.data() + .stream() + .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()), + new OpenAiImageGenerationMetadata(entry.revisedPrompt()))) + .toList(); + + ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created()); + return new ImageResponse(imageGenerationList, openAiImageResponseMetadata); + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java new file mode 100644 index 0000000000..8af0cbeea8 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.framework.ai.core.model.siliconflow; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import org.springframework.ai.image.ImageOptions; +import org.springframework.ai.openai.OpenAiImageOptions; + +/** + * 硅基流动画图能力 + * + * @author zzt + */ +@Data +public class SiliconflowImageOptions implements ImageOptions { + + @JsonProperty("model") + private String model; + + @JsonProperty("negative_prompt") + private String negativePrompt; + + /** + * The number of images to generate. Must be between 1 and 4. + */ + @JsonProperty("image_size") + private String imageSize; + + /** + * The number of images to generate. Must be between 1 and 4. + */ + @JsonProperty("batch_size") + private Integer batchSize = 1; + + /** + * number of inference steps + */ + @JsonProperty("num_inference_steps") + private Integer numInferenceSteps = 25; + + + /** + * This value is used to control the degree of match between the generated image and the given prompt. The higher the value, the more the generated image will tend to strictly match the text prompt. The lower the value, the more creative and diverse the generated image will be, potentially containing more unexpected elements. + * + * Required range: 0 <= x <= 20 + */ + @JsonProperty("guidance_scale") + private Float guidanceScale = 0.75F; + + /** + * 如果想要每次都生成固定的图片,可以把seed设置为固定值。 + * + */ + @JsonProperty("seed") + private Integer seed = (int)(Math.random() * 1_000_000_000); + + /** + * The image that needs to be uploaded should be converted into base64 format. + */ + @JsonProperty("image") + private String image; + + + /** + * 宽 + */ + private Integer width; + + + /** + * 高 + */ + private Integer height; + + public void setHeight(Integer height) { + this.height = height; + if (this.width != null && this.height != null) { + this.imageSize = this.width + "x" + this.height; + } + } + + public void setWidth(Integer width) { + this.width = width; + if (this.width != null && this.height != null) { + this.imageSize = this.width + "x" + this.height; + } + } + + /** + * 硅基流动 + * @return + */ + public static SiliconflowImageOptions.Builder builder() { + return new SiliconflowImageOptions.Builder(); + } + + @Override + public String toString() { + + return "SiliconflowImageOptions{" + "model='" + getModel() + '\'' + ", batch_size=" + batchSize + ", imageSize=" + imageSize + ", negativePrompt='" + + negativePrompt + '\'' + '}'; + } + + @Override + public Integer getN() { + return null; + } + + @Override + public String getResponseFormat() { + return null; + } + + @Override + public String getStyle() { + return null; + } + + public static class Builder extends OpenAiImageOptions{ + + private final SiliconflowImageOptions options; + + private Builder() { + this.options = new SiliconflowImageOptions(); + } + + public SiliconflowImageOptions.Builder model(String model) { + this.options.setModel(model); + return this; + } + + public SiliconflowImageOptions.Builder withBatchSize(Integer batchSize) { + options.setBatchSize(batchSize); + return this; + } + + public SiliconflowImageOptions.Builder withModel(String model) { + options.setModel(model); + return this; + } + + public SiliconflowImageOptions.Builder withWidth(Integer width) { + options.setWidth(width); + return this; + } + + public SiliconflowImageOptions.Builder withHeight(Integer height) { + options.setHeight(height); + return this; + } + + public SiliconflowImageOptions.Builder withSeed(Integer seed) { + options.setSeed(seed); + return this; + } + + public SiliconflowImageOptions.Builder withNegativePrompt(String negativePrompt) { + options.setNegativePrompt(negativePrompt); + return this; + } + + public SiliconflowImageOptions build() { + return options; + } + + } +} 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..8ca8772a4d 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 @@ -50,8 +50,8 @@ public class AiUtils { case HUN_YUAN: // 复用 OpenAI 客户端 case XING_HUO: // 复用 OpenAI 客户端 case SILICON_FLOW: // 复用 OpenAI 客户端 - return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) - .toolNames(toolNames).build(); + OpenAiChatOptions.Builder builder = OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens); + return toolNames == null ? builder.build() : builder.toolNames(toolNames).build(); case AZURE_OPENAI: // TODO 芋艿:貌似没 model 字段???! return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java index 880795fe96..1344a7179f 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.ai.chat; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -25,11 +26,11 @@ public class SiliconFlowChatModelTests { private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() - .baseUrl(SiliconFlowChatModel.BASE_URL) + .baseUrl(SiiconflowApiConstants.DEFAULT_BASE_URL) .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey .build()) .defaultOptions(OpenAiChatOptions.builder() - .model(SiliconFlowChatModel.MODEL_DEFAULT) // 模型 + .model(SiiconflowApiConstants.MODEL_DEFAULT) // 模型 // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费 // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费 .temperature(0.7) From ecf4df86204e278cc1c66f788947b4eb9906bc87 Mon Sep 17 00:00:00 2001 From: zzt <976209226@qq.com> Date: Thu, 20 Mar 2025 08:04:32 +0800 Subject: [PATCH 376/386] =?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=85=BE=E8=AE=AF=E5=9B=BE?= =?UTF-8?q?=E5=83=8F=E5=88=9B=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../creation/AiartCreationController.java | 36 ++++++++++ .../admin/creation/AiartStyleController.java | 40 ++++++++++++ .../vo/style/AiartReplaceBackgroundReqVO.java | 17 +++++ .../vo/style/AiartReplaceBackgroundResVO.java | 18 +++++ .../vo/style/BackgroundTemplateReqVO.java | 16 +++++ .../vo/style/BackgroundTemplateResVO.java | 28 ++++++++ .../AiartStyleBackgroundTemplateDO.java | 65 +++++++++++++++++++ .../AiartStyleBackgroundTemplateMapper.java | 33 ++++++++++ .../creation/AiartCreationService.java | 21 ++++++ .../creation/AiartCreationServiceImpl.java | 22 +++++++ .../service/creation/AiartStyleService.java | 23 +++++++ .../creation/AiartStyleServiceImpl.java | 27 ++++++++ .../yudao-spring-boot-starter-ai/pom.xml | 7 ++ 13 files changed, 353 insertions(+) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java new file mode 100644 index 0000000000..64605d7177 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.ai.controller.admin.creation; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO; +import cn.iocoder.yudao.module.ai.service.creation.AiartCreationService; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 艺术创作") +@RestController +@RequestMapping("/ai/artcreation") +@Validated +public class AiartCreationController { + + @Resource + private AiartCreationService aiartCreationService; + + @Operation(summary = "商品图替换背景") + @PostMapping("/replacebackground") + public CommonResult replaceBackground(@Valid @RequestBody AiartReplaceBackgroundReqVO replaceBackgroundReqVO) { + return success(aiartCreationService.replaceBackground(getLoginUserId(), replaceBackgroundReqVO)); + } + + +} \ 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/creation/AiartStyleController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java new file mode 100644 index 0000000000..4b70525e4a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.ai.controller.admin.creation; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; +import cn.iocoder.yudao.module.ai.service.creation.AiartStyleService; +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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +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 = "管理后台 - 艺术创作模版") +@RestController +@RequestMapping("/ai/artstyle") +@Validated +public class AiartStyleController { + + @Resource + private AiartStyleService aiartStyleService; + + @PostMapping("/backgroundtemplate-list") + @Operation(summary = "获取商品背景模版列表") + @PreAuthorize("@ss.hasPermission('ai:artstyle-background:query')") + public CommonResult> queryBackgroundStyle(@Valid @RequestBody BackgroundTemplateReqVO backgroundTemplateReqVO) { + List res = aiartStyleService.queryBackgroundStyle(backgroundTemplateReqVO); + return success(BeanUtils.toBean(res,BackgroundTemplateResVO.class)); + } +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java new file mode 100644 index 0000000000..8f2e9f9284 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - 商品替换背景请求 VO") +@Data +public class AiartReplaceBackgroundReqVO { + + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城") + @NotEmpty(message = "提示词不能为空") + @Size(max = 1200, message = "提示词最大 1200") + private String prompt; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java new file mode 100644 index 0000000000..43ec35c774 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 商品替换背景结果 VO") +@Data +public class AiartReplaceBackgroundResVO { + + @Schema(description = "结果url", requiredMode = Schema.RequiredMode.REQUIRED, example = "根据参数不同不一样") + private String resultImage; + + @Schema(description = "唯一请求 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "由服务端生成") + private String requestId; + + @Schema(description = "MaskUrl", requiredMode = Schema.RequiredMode.REQUIRED, example = "如果 MaskUrl 未传,则返回使用内置商品分割算法得到的 Mask 结果") + private String maskImage; +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java new file mode 100644 index 0000000000..42ad20c174 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO") +@Data +public class BackgroundTemplateReqVO { + + @Schema(description = "一级级次名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "通用") + private String fistLevelName; + + @Schema(description = "二级级次名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "色彩背景") + private String secondLevelName; + +} \ 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/creation/vo/style/BackgroundTemplateResVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java new file mode 100644 index 0000000000..2fb0ae3ac0 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 商品替换背景模版 VO") +@Data +public class BackgroundTemplateResVO { + + @Schema(description = "一级分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "汽车、通用") + private String levelFirst; + + @Schema(description = "二级分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "色彩背景") + private String levelSecond; + + @Schema(description = "背景模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "彩虹渐变") + private String name; + + @Schema(description = "背景模版提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "背景为彩虹渐变墙面,亮暖色,清新简约风格,喷涂效果") + private String prompt; + + @Schema(description = "背景模版商品示例", requiredMode = Schema.RequiredMode.REQUIRED, example = "小熊玩偶") + private String demProduct; + + @Schema(description = "背景模版商品示例效果", requiredMode = Schema.RequiredMode.REQUIRED, example = "url") + private String demUrl; +} + diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java new file mode 100644 index 0000000000..0cc538efca --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.creation; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * AI art商品背景风格 DO + * + * @author 芋道源码 + */ +@TableName("ai_backgroundtemplate") +@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AiartStyleBackgroundTemplateDO extends BaseDO { + /** + * 编号 + */ + @TableId + private Long id; + + /** + * 名称 + */ + private String levelFirst; + + /** + * 名称 + */ + private String levelSecond; + /** + * 模版名称 + */ + private String name; + /** + * 模版提示词 + */ + private String prompt; + /** + * 示例商品 + */ + private String demoProduct; + + /** + * 示例效果 + */ + private String demoUrl; + + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java new file mode 100644 index 0000000000..a0ac51a7e6 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.creation; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +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.creation.vo.style.BackgroundTemplateReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * AI style分隔 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiartStyleBackgroundTemplateMapper extends BaseMapperX { + + /** + * 获取有效的商品后端模版 + * @param reqVO 请求对象 + * @return 结果 + */ + default List selectList(BackgroundTemplateReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(AiartStyleBackgroundTemplateDO::getLevelFirst,reqVO.getFistLevelName()) + .eqIfPresent(AiartStyleBackgroundTemplateDO::getLevelSecond,reqVO.getSecondLevelName()) + .eqIfPresent(AiartStyleBackgroundTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) + .orderByDesc(AiartStyleBackgroundTemplateDO::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/creation/AiartCreationService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java new file mode 100644 index 0000000000..e2300a8082 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.ai.service.creation; + +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO; + +/** + * AI创作Service 接口 + * + * @author zzt + */ +public interface AiartCreationService { + + + /** + * 替换商品背景图片 + * @param loginUserId 当前登录用户 + * @param replaceBackgroundReqVO 请求对象 + * @return 结果 + */ + AiartReplaceBackgroundResVO replaceBackground(Long loginUserId, AiartReplaceBackgroundReqVO replaceBackgroundReqVO); +} \ 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/creation/AiartCreationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java new file mode 100644 index 0000000000..f4f14cab19 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.ai.service.creation; + +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * AI创作Service 实现类 + * + * @author zzt + */ +@Service +@Validated +public class AiartCreationServiceImpl implements AiartCreationService { + + + @Override + public AiartReplaceBackgroundResVO replaceBackground(Long loginUserId, AiartReplaceBackgroundReqVO replaceBackgroundReqVO) { + return null; + } +} \ 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/creation/AiartStyleService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java new file mode 100644 index 0000000000..635577086a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.ai.service.creation; + +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; + +import java.util.List; + +/** + * AI风格Service 接口 + * + * @author zzt + */ +public interface AiartStyleService { + + + /** + * 查询商品背景模版列表 + * @param backgroundTemplateReqVO 商品背景模版请求对象 + * @return 结果 + */ + List queryBackgroundStyle(BackgroundTemplateReqVO backgroundTemplateReqVO); +} \ 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/creation/AiartStyleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java new file mode 100644 index 0000000000..9b85b0788f --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.service.creation; + +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; +import cn.iocoder.yudao.module.ai.dal.mysql.creation.AiartStyleBackgroundTemplateMapper; +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; +import jakarta.annotation.Resource; + +import java.util.List; + +/** + * AI风格Service 接口 + * + * @author zzt + */ +public class AiartStyleServiceImpl implements AiartStyleService{ + + @Resource + private AiartStyleBackgroundTemplateMapper backgroundTemplateMapper; + + + @Override + public List queryBackgroundStyle(BackgroundTemplateReqVO backgroundTemplateReqVO) { + return backgroundTemplateMapper.selectList(backgroundTemplateReqVO); + } +} \ 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 f37f3709c6..a433d9f94f 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -117,6 +117,13 @@ + + + com.tencentcloudapi + tencentcloud-sdk-java-aiart + 3.1.1215 + + org.springframework.boot From 9b11199665f00790ecabad6e10f449c4148635f1 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 22 Mar 2025 09:41:22 +0800 Subject: [PATCH 377/386] =?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 378/386] =?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 813e7af8467c1f4b693593fdbe9c20b96a0228e6 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 10:27:01 +0800 Subject: [PATCH 379/386] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91AI=EF=BC=9A=E7=A1=85=E5=9F=BA=E6=B5=81?= =?UTF-8?q?=E5=8A=A8=E7=9A=84=E5=9B=BE=E7=89=87=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../creation/AiartCreationController.java | 36 ---------- .../admin/creation/AiartStyleController.java | 40 ------------ .../vo/style/AiartReplaceBackgroundReqVO.java | 17 ----- .../vo/style/AiartReplaceBackgroundResVO.java | 18 ----- .../vo/style/BackgroundTemplateReqVO.java | 16 ----- .../vo/style/BackgroundTemplateResVO.java | 28 -------- .../AiartStyleBackgroundTemplateDO.java | 65 ------------------- .../AiartStyleBackgroundTemplateMapper.java | 33 ---------- .../creation/AiartCreationService.java | 21 ------ .../creation/AiartCreationServiceImpl.java | 22 ------- .../service/creation/AiartStyleService.java | 23 ------- .../creation/AiartStyleServiceImpl.java | 27 -------- .../ai/service/image/AiImageServiceImpl.java | 4 +- .../yudao-spring-boot-starter-ai/pom.xml | 7 -- .../ai/config/YudaoAiAutoConfiguration.java | 1 - .../yudao/framework/ai/core/util/AiUtils.java | 4 +- .../ai/image/SiliconFlowImageModelTests.java | 35 ++++++++++ 17 files changed, 39 insertions(+), 358 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java deleted file mode 100644 index 64605d7177..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartCreationController.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.creation; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO; -import cn.iocoder.yudao.module.ai.service.creation.AiartCreationService; -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.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 艺术创作") -@RestController -@RequestMapping("/ai/artcreation") -@Validated -public class AiartCreationController { - - @Resource - private AiartCreationService aiartCreationService; - - @Operation(summary = "商品图替换背景") - @PostMapping("/replacebackground") - public CommonResult replaceBackground(@Valid @RequestBody AiartReplaceBackgroundReqVO replaceBackgroundReqVO) { - return success(aiartCreationService.replaceBackground(getLoginUserId(), replaceBackgroundReqVO)); - } - - -} \ 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/creation/AiartStyleController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java deleted file mode 100644 index 4b70525e4a..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/AiartStyleController.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.creation; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; -import cn.iocoder.yudao.module.ai.service.creation.AiartStyleService; -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.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -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 = "管理后台 - 艺术创作模版") -@RestController -@RequestMapping("/ai/artstyle") -@Validated -public class AiartStyleController { - - @Resource - private AiartStyleService aiartStyleService; - - @PostMapping("/backgroundtemplate-list") - @Operation(summary = "获取商品背景模版列表") - @PreAuthorize("@ss.hasPermission('ai:artstyle-background:query')") - public CommonResult> queryBackgroundStyle(@Valid @RequestBody BackgroundTemplateReqVO backgroundTemplateReqVO) { - List res = aiartStyleService.queryBackgroundStyle(backgroundTemplateReqVO); - return success(BeanUtils.toBean(res,BackgroundTemplateResVO.class)); - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java deleted file mode 100644 index 8f2e9f9284..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundReqVO.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.Size; -import lombok.Data; - -@Schema(description = "管理后台 - 商品替换背景请求 VO") -@Data -public class AiartReplaceBackgroundReqVO { - - @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城") - @NotEmpty(message = "提示词不能为空") - @Size(max = 1200, message = "提示词最大 1200") - private String prompt; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java deleted file mode 100644 index 43ec35c774..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/AiartReplaceBackgroundResVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 商品替换背景结果 VO") -@Data -public class AiartReplaceBackgroundResVO { - - @Schema(description = "结果url", requiredMode = Schema.RequiredMode.REQUIRED, example = "根据参数不同不一样") - private String resultImage; - - @Schema(description = "唯一请求 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "由服务端生成") - private String requestId; - - @Schema(description = "MaskUrl", requiredMode = Schema.RequiredMode.REQUIRED, example = "如果 MaskUrl 未传,则返回使用内置商品分割算法得到的 Mask 结果") - private String maskImage; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java deleted file mode 100644 index 42ad20c174..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateReqVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO") -@Data -public class BackgroundTemplateReqVO { - - @Schema(description = "一级级次名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "通用") - private String fistLevelName; - - @Schema(description = "二级级次名称", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "色彩背景") - private String secondLevelName; - -} \ 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/creation/vo/style/BackgroundTemplateResVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java deleted file mode 100644 index 2fb0ae3ac0..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/creation/vo/style/BackgroundTemplateResVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 商品替换背景模版 VO") -@Data -public class BackgroundTemplateResVO { - - @Schema(description = "一级分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "汽车、通用") - private String levelFirst; - - @Schema(description = "二级分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "色彩背景") - private String levelSecond; - - @Schema(description = "背景模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "彩虹渐变") - private String name; - - @Schema(description = "背景模版提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "背景为彩虹渐变墙面,亮暖色,清新简约风格,喷涂效果") - private String prompt; - - @Schema(description = "背景模版商品示例", requiredMode = Schema.RequiredMode.REQUIRED, example = "小熊玩偶") - private String demProduct; - - @Schema(description = "背景模版商品示例效果", requiredMode = Schema.RequiredMode.REQUIRED, example = "url") - private String demUrl; -} - diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java deleted file mode 100644 index 0cc538efca..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/creation/AiartStyleBackgroundTemplateDO.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.dataobject.creation; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -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.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * AI art商品背景风格 DO - * - * @author 芋道源码 - */ -@TableName("ai_backgroundtemplate") -@KeySequence("ai_chat_conversation_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AiartStyleBackgroundTemplateDO extends BaseDO { - /** - * 编号 - */ - @TableId - private Long id; - - /** - * 名称 - */ - private String levelFirst; - - /** - * 名称 - */ - private String levelSecond; - /** - * 模版名称 - */ - private String name; - /** - * 模版提示词 - */ - private String prompt; - /** - * 示例商品 - */ - private String demoProduct; - - /** - * 示例效果 - */ - private String demoUrl; - - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java deleted file mode 100644 index a0ac51a7e6..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/creation/AiartStyleBackgroundTemplateMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.ai.dal.mysql.creation; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -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.creation.vo.style.BackgroundTemplateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * AI style分隔 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface AiartStyleBackgroundTemplateMapper extends BaseMapperX { - - /** - * 获取有效的商品后端模版 - * @param reqVO 请求对象 - * @return 结果 - */ - default List selectList(BackgroundTemplateReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(AiartStyleBackgroundTemplateDO::getLevelFirst,reqVO.getFistLevelName()) - .eqIfPresent(AiartStyleBackgroundTemplateDO::getLevelSecond,reqVO.getSecondLevelName()) - .eqIfPresent(AiartStyleBackgroundTemplateDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) - .orderByDesc(AiartStyleBackgroundTemplateDO::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/creation/AiartCreationService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java deleted file mode 100644 index e2300a8082..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationService.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.creation; - -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO; - -/** - * AI创作Service 接口 - * - * @author zzt - */ -public interface AiartCreationService { - - - /** - * 替换商品背景图片 - * @param loginUserId 当前登录用户 - * @param replaceBackgroundReqVO 请求对象 - * @return 结果 - */ - AiartReplaceBackgroundResVO replaceBackground(Long loginUserId, AiartReplaceBackgroundReqVO replaceBackgroundReqVO); -} \ 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/creation/AiartCreationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java deleted file mode 100644 index f4f14cab19..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartCreationServiceImpl.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.creation; - -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.AiartReplaceBackgroundResVO; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -/** - * AI创作Service 实现类 - * - * @author zzt - */ -@Service -@Validated -public class AiartCreationServiceImpl implements AiartCreationService { - - - @Override - public AiartReplaceBackgroundResVO replaceBackground(Long loginUserId, AiartReplaceBackgroundReqVO replaceBackgroundReqVO) { - return null; - } -} \ 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/creation/AiartStyleService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java deleted file mode 100644 index 635577086a..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleService.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.creation; - -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; - -import java.util.List; - -/** - * AI风格Service 接口 - * - * @author zzt - */ -public interface AiartStyleService { - - - /** - * 查询商品背景模版列表 - * @param backgroundTemplateReqVO 商品背景模版请求对象 - * @return 结果 - */ - List queryBackgroundStyle(BackgroundTemplateReqVO backgroundTemplateReqVO); -} \ 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/creation/AiartStyleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java deleted file mode 100644 index 9b85b0788f..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/creation/AiartStyleServiceImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.creation; - -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.creation.vo.style.BackgroundTemplateResVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.creation.AiartStyleBackgroundTemplateDO; -import cn.iocoder.yudao.module.ai.dal.mysql.creation.AiartStyleBackgroundTemplateMapper; -import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; -import jakarta.annotation.Resource; - -import java.util.List; - -/** - * AI风格Service 接口 - * - * @author zzt - */ -public class AiartStyleServiceImpl implements AiartStyleService{ - - @Resource - private AiartStyleBackgroundTemplateMapper backgroundTemplateMapper; - - - @Override - public List queryBackgroundStyle(BackgroundTemplateReqVO backgroundTemplateReqVO) { - return backgroundTemplateMapper.selectList(backgroundTemplateReqVO); - } -} \ 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/image/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java index f0ef418301..caa84e7c63 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 @@ -147,8 +147,8 @@ public class AiImageServiceImpl implements AiImageService { .build(); } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) { // https://docs.siliconflow.cn/cn/api-reference/images/images-generations - return SiliconflowImageOptions.builder().withModel(model.getModel()) - .withHeight(draw.getHeight()).withWidth(draw.getWidth()) + return SiliconflowImageOptions.builder().model(model.getModel()) + .withHeight(draw.getHeight()).withHeight(draw.getWidth()) .build(); } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage 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 a433d9f94f..f37f3709c6 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -117,13 +117,6 @@ - - - com.tencentcloudapi - tencentcloud-sdk-java-aiart - 3.1.1215 - - org.springframework.boot 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 dc1846ef99..a9fca193ad 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 @@ -9,7 +9,6 @@ import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi; 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; 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 8ca8772a4d..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 @@ -50,8 +50,8 @@ public class AiUtils { case HUN_YUAN: // 复用 OpenAI 客户端 case XING_HUO: // 复用 OpenAI 客户端 case SILICON_FLOW: // 复用 OpenAI 客户端 - OpenAiChatOptions.Builder builder = OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens); - return toolNames == null ? builder.build() : builder.toolNames(toolNames).build(); + return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) + .toolNames(toolNames).build(); case AZURE_OPENAI: // TODO 芋艿:貌似没 model 字段???! return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java new file mode 100644 index 0000000000..40ad3d3ede --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.framework.ai.image; + +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageModel; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageOptions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.ai.image.ImagePrompt; +import org.springframework.ai.image.ImageResponse; + +/** + * {@link SiliconflowImageModel} 集成测试 + */ +public class SiliconFlowImageModelTests { + + private final SiliconflowImageModel imageModel = new SiliconflowImageModel( + new SiiconflowmageApi("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // 密钥 + ); + + @Test + @Disabled + public void testCall() { + // 准备参数 + SiliconflowImageOptions imageOptions = SiliconflowImageOptions.builder() + .model("Kwai-Kolors/Kolors") + .build(); + ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions); + + // 方法调用 + ImageResponse response = imageModel.call(prompt); + // 打印结果 + System.out.println(response); + } + +} From 59c744520ad3d19e87aa830f2cc3cb761d30daef Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 10:41:51 +0800 Subject: [PATCH 380/386] =?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=A1=85=E5=9F=BA=E6=B5=81?= =?UTF-8?q?=E5=8A=A8=E7=9A=84=E5=9B=BE=E7=89=87=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/service/image/AiImageServiceImpl.java | 6 +- .../ai/config/YudaoAiAutoConfiguration.java | 6 +- .../ai/core/factory/AiModelFactoryImpl.java | 22 +- .../model/siliconflow/SiiconflowmageApi.java | 207 ------------------ ...ants.java => SiliconFlowApiConstants.java} | 8 +- .../siliconflow/SiliconFlowImageApi.java | 115 ++++++++++ ...eModel.java => SiliconFlowImageModel.java} | 80 ++----- ...ions.java => SiliconFlowImageOptions.java} | 79 +------ .../ai/chat/SiliconFlowChatModelTests.java | 6 +- .../ai/image/SiliconFlowImageModelTests.java | 14 +- 10 files changed, 179 insertions(+), 364 deletions(-) delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java rename yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/{SiiconflowApiConstants.java => SiliconFlowApiConstants.java} (87%) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java rename yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/{SiliconflowImageModel.java => SiliconFlowImageModel.java} (60%) rename yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/{SiliconflowImageOptions.java => SiliconFlowImageOptions.java} (52%) 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 caa84e7c63..c6c9fa43c1 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 @@ -11,7 +11,7 @@ import cn.hutool.extra.spring.SpringUtil; import cn.hutool.http.HttpUtil; 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.siliconflow.SiliconflowImageOptions; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowImageOptions; 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.AiImageDrawReqVO; @@ -147,8 +147,8 @@ public class AiImageServiceImpl implements AiImageService { .build(); } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) { // https://docs.siliconflow.cn/cn/api-reference/images/images-generations - return SiliconflowImageOptions.builder().model(model.getModel()) - .withHeight(draw.getHeight()).withHeight(draw.getWidth()) + return SiliconFlowImageOptions.builder().model(model.getModel()) + .height(draw.getHeight()).width(draw.getWidth()) .build(); } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) { // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage 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 a9fca193ad..e014a4cd9f 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 @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; 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; @@ -114,11 +114,11 @@ public class YudaoAiAutoConfiguration { public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlowProperties properties) { if (StrUtil.isEmpty(properties.getModel())) { - properties.setModel(SiiconflowApiConstants.MODEL_DEFAULT); + properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT); } OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() - .baseUrl(SiiconflowApiConstants.DEFAULT_BASE_URL) + .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) .apiKey(properties.getApiKey()) .build()) .defaultOptions(OpenAiChatOptions.builder() 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 6c72b2b5f3..77ff76486a 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 @@ -15,10 +15,10 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageModel; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowImageApi; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowImageModel; 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; @@ -45,6 +45,7 @@ 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; +import org.springframework.ai.autoconfigure.stabilityai.StabilityAiImageAutoConfiguration; import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientConnectionDetails; import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration; @@ -228,7 +229,7 @@ public class AiModelFactoryImpl implements AiModelFactory { case OPENAI: return buildOpenAiImageModel(apiKey, url); case SILICON_FLOW: - return buildSiiconflowImageModel(apiKey,url); + return buildSiliconFlowImageModel(apiKey,url); case STABLE_DIFFUSION: return buildStabilityAiImageModel(apiKey, url); default: @@ -474,12 +475,12 @@ public class AiModelFactoryImpl implements AiModelFactory { } /** - * Siiconflow + * 创建 SiliconFlowImageModel 对象 */ - private SiliconflowImageModel buildSiiconflowImageModel(String apiToken, String url) { - url = StrUtil.blankToDefault(url, SiiconflowApiConstants.DEFAULT_BASE_URL); - SiiconflowmageApi openAiApi = SiiconflowmageApi.builder().baseUrl(url).apiKey(apiToken).build(); - return new SiliconflowImageModel(openAiApi); + private SiliconFlowImageModel buildSiliconFlowImageModel(String apiToken, String url) { + url = StrUtil.blankToDefault(url, SiliconFlowApiConstants.DEFAULT_BASE_URL); + SiliconFlowImageApi openAiApi = new SiliconFlowImageApi(url, apiToken); + return new SiliconFlowImageModel(openAiApi); } /** @@ -490,6 +491,9 @@ public class AiModelFactoryImpl implements AiModelFactory { return OllamaChatModel.builder().ollamaApi(ollamaApi).toolCallingManager(getToolCallingManager()).build(); } + /** + * 可参考 {@link StabilityAiImageAutoConfiguration} 的 stabilityAiImageModel 方法 + */ private StabilityAiImageModel buildStabilityAiImageModel(String apiKey, String url) { url = StrUtil.blankToDefault(url, StabilityAiApi.DEFAULT_BASE_URL); StabilityAiApi stabilityAiApi = new StabilityAiApi(apiKey, StabilityAiApi.DEFAULT_IMAGE_MODEL, url); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java deleted file mode 100644 index 8d0cf925af..0000000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowmageApi.java +++ /dev/null @@ -1,207 +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 cn.iocoder.yudao.framework.ai.core.model.siliconflow; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.springframework.ai.model.ApiKey; -import org.springframework.ai.model.NoopApiKey; -import org.springframework.ai.model.SimpleApiKey; -import org.springframework.ai.openai.api.OpenAiImageApi; -import org.springframework.ai.retry.RetryUtils; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.ResponseErrorHandler; -import org.springframework.web.client.RestClient; - -import java.util.Map; - -/** - * Siiconflow Image API. - * - * @see Images - * - * @author zzt - */ -public class SiiconflowmageApi { - - private final RestClient restClient; - - /** - * Create a new Siiconflow Image api with base URL set. - * @param aiToken OpenAI apiKey. - */ - public SiiconflowmageApi(String aiToken) { - this(SiiconflowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder()); - } - - /** - * Create a new Siiconflow Image API with the provided base URL. - * @param baseUrl the base URL for the OpenAI API. - * @param openAiToken Siiconflow apiKey. - */ - public SiiconflowmageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) { - this(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER); - } - - /** - * Create a new OpenAI Image API with the provided base URL. - * @param baseUrl the base URL for the OpenAI API. - * @param apiKey OpenAI apiKey. - * @param restClientBuilder the rest client builder to use. - */ - public SiiconflowmageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder, - ResponseErrorHandler responseErrorHandler) { - this(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler); - } - - /** - * Create a new OpenAI Image API with the provided base URL. - * @param baseUrl the base URL for the OpenAI API. - * @param apiKey OpenAI apiKey. - * @param headers the http headers to use. - * @param restClientBuilder the rest client builder to use. - * @param responseErrorHandler the response error handler to use. - */ - public SiiconflowmageApi(String baseUrl, String apiKey, MultiValueMap headers, - RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { - - this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler); - } - - /** - * Create a new OpenAI Image API with the provided base URL. - * @param baseUrl the base URL for the OpenAI API. - * @param apiKey OpenAI apiKey. - * @param headers the http headers to use. - * @param restClientBuilder the rest client builder to use. - * @param responseErrorHandler the response error handler to use. - */ - public SiiconflowmageApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, - RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { - - // @formatter:off - this.restClient = restClientBuilder.baseUrl(baseUrl) - .defaultHeaders(h -> { - if(!(apiKey instanceof NoopApiKey)) { - h.setBearerAuth(apiKey.getValue()); - } - h.setContentType(MediaType.APPLICATION_JSON); - h.addAll(headers); - }) - .defaultStatusHandler(responseErrorHandler) - .build(); - // @formatter:on - } - - public ResponseEntity createImage(SiliconflowImageRequest siliconflowImageRequest) { - Assert.notNull(siliconflowImageRequest, "Image request cannot be null."); - Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty."); - - return this.restClient.post() - .uri("v1/images/generations") - .body(siliconflowImageRequest) - .retrieve() - .toEntity(OpenAiImageApi.OpenAiImageResponse.class); - } - - - // @formatter:off - @JsonInclude(JsonInclude.Include.NON_NULL) - public record SiliconflowImageRequest ( - @JsonProperty("prompt") String prompt, - @JsonProperty("model") String model, - @JsonProperty("batch_size") Integer batchSize, - @JsonProperty("negative_prompt") String negativePrompt, - @JsonProperty("seed") Integer seed, - @JsonProperty("num_inference_steps") Integer numInferenceSteps, - @JsonProperty("guidance_scale") Float guidanceScale, - @JsonProperty("image") String image) { - - public SiliconflowImageRequest(String prompt, String model) { - this(prompt, model, null, null, null, null, null, null); - } - } - - public static Builder builder() { - return new Builder(); - } - - /** - * Builder to construct {@link SiiconflowmageApi} instance. - */ - public static class Builder { - - private String baseUrl = SiiconflowApiConstants.DEFAULT_BASE_URL; - - private ApiKey apiKey; - - private MultiValueMap headers = new LinkedMultiValueMap<>(); - - private RestClient.Builder restClientBuilder = RestClient.builder(); - - private ResponseErrorHandler responseErrorHandler = RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER; - - public Builder baseUrl(String baseUrl) { - Assert.hasText(baseUrl, "baseUrl cannot be null or empty"); - this.baseUrl = baseUrl; - return this; - } - - public Builder apiKey(ApiKey apiKey) { - Assert.notNull(apiKey, "apiKey cannot be null"); - this.apiKey = apiKey; - return this; - } - - public Builder apiKey(String simpleApiKey) { - Assert.notNull(simpleApiKey, "simpleApiKey cannot be null"); - this.apiKey = new SimpleApiKey(simpleApiKey); - return this; - } - - public Builder headers(MultiValueMap headers) { - Assert.notNull(headers, "headers cannot be null"); - this.headers = headers; - return this; - } - - public Builder restClientBuilder(RestClient.Builder restClientBuilder) { - Assert.notNull(restClientBuilder, "restClientBuilder cannot be null"); - this.restClientBuilder = restClientBuilder; - return this; - } - - public Builder responseErrorHandler(ResponseErrorHandler responseErrorHandler) { - Assert.notNull(responseErrorHandler, "responseErrorHandler cannot be null"); - this.responseErrorHandler = responseErrorHandler; - return this; - } - - public SiiconflowmageApi build() { - Assert.notNull(this.apiKey, "apiKey must be set"); - return new SiiconflowmageApi(this.baseUrl, this.apiKey, this.headers, this.restClientBuilder, - this.responseErrorHandler); - } - - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java similarity index 87% rename from yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java rename to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java index 589d05855d..25bf4a1755 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiiconflowApiConstants.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java @@ -17,11 +17,11 @@ package cn.iocoder.yudao.framework.ai.core.model.siliconflow; /** - * Common value constants for Siiconflow api. + * SiliconFlow API 枚举类 * * @author zzt */ -public final class SiiconflowApiConstants { +public final class SiliconFlowApiConstants { public static final String DEFAULT_BASE_URL = "https://api.siliconflow.cn"; @@ -29,8 +29,4 @@ public final class SiiconflowApiConstants { public static final String PROVIDER_NAME = "Siiconflow"; - private SiiconflowApiConstants() { - - } - } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java new file mode 100644 index 0000000000..1408fbe2e4 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java @@ -0,0 +1,115 @@ +/* + * 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 cn.iocoder.yudao.framework.ai.core.model.siliconflow; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.ai.model.ApiKey; +import org.springframework.ai.model.NoopApiKey; +import org.springframework.ai.model.SimpleApiKey; +import org.springframework.ai.openai.api.OpenAiImageApi; +import org.springframework.ai.retry.RetryUtils; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestClient; + +import java.util.Map; + +/** + * 硅基流动 Image API + * + * @see Images + * + * @author zzt + */ +public class SiliconFlowImageApi { + + private final RestClient restClient; + + public SiliconFlowImageApi(String aiToken) { + this(SiliconFlowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder()); + } + + public SiliconFlowImageApi(String baseUrl, String openAiToken) { + this(baseUrl, openAiToken, RestClient.builder()); + } + + public SiliconFlowImageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) { + this(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER); + } + + public SiliconFlowImageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder, + ResponseErrorHandler responseErrorHandler) { + this(baseUrl, apiKey, CollectionUtils.toMultiValueMap(Map.of()), restClientBuilder, responseErrorHandler); + } + + public SiliconFlowImageApi(String baseUrl, String apiKey, MultiValueMap headers, + RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { + this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler); + } + + public SiliconFlowImageApi(String baseUrl, ApiKey apiKey, MultiValueMap headers, + RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) { + + // @formatter:off + this.restClient = restClientBuilder.baseUrl(baseUrl) + .defaultHeaders(h -> { + if(!(apiKey instanceof NoopApiKey)) { + h.setBearerAuth(apiKey.getValue()); + } + h.setContentType(MediaType.APPLICATION_JSON); + h.addAll(headers); + }) + .defaultStatusHandler(responseErrorHandler) + .build(); + // @formatter:on + } + + public ResponseEntity createImage(SiliconflowImageRequest siliconflowImageRequest) { + Assert.notNull(siliconflowImageRequest, "Image request cannot be null."); + Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty."); + + return this.restClient.post() + .uri("v1/images/generations") + .body(siliconflowImageRequest) + .retrieve() + .toEntity(OpenAiImageApi.OpenAiImageResponse.class); + } + + + // @formatter:off + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SiliconflowImageRequest ( + @JsonProperty("prompt") String prompt, + @JsonProperty("model") String model, + @JsonProperty("batch_size") Integer batchSize, + @JsonProperty("negative_prompt") String negativePrompt, + @JsonProperty("seed") Integer seed, + @JsonProperty("num_inference_steps") Integer numInferenceSteps, + @JsonProperty("guidance_scale") Float guidanceScale, + @JsonProperty("image") String image) { + + public SiliconflowImageRequest(String prompt, String model) { + this(prompt, model, null, null, null, null, null, null); + } + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java similarity index 60% rename from yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java rename to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java index 0e137da726..1e3965ed91 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageModel.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java @@ -17,6 +17,7 @@ package cn.iocoder.yudao.framework.ai.core.model.siliconflow; import io.micrometer.observation.ObservationRegistry; +import lombok.Setter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.image.*; @@ -25,8 +26,8 @@ import org.springframework.ai.image.observation.ImageModelObservationContext; import org.springframework.ai.image.observation.ImageModelObservationConvention; import org.springframework.ai.image.observation.ImageModelObservationDocumentation; import org.springframework.ai.model.ModelOptionsUtils; +import org.springframework.ai.openai.OpenAiImageModel; import org.springframework.ai.openai.api.OpenAiImageApi; -import org.springframework.ai.openai.api.common.OpenAiApiConstants; import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata; import org.springframework.ai.retry.RetryUtils; import org.springframework.http.ResponseEntity; @@ -36,77 +37,44 @@ import org.springframework.util.Assert; import java.util.List; /** - * cv openapi图片模型方法 + * 硅基流动 {@link ImageModel} 实现类 + * + * 参考 {@link OpenAiImageModel} 实现 * * @author zzt */ -public class SiliconflowImageModel implements ImageModel { +public class SiliconFlowImageModel implements ImageModel { - private static final Logger logger = LoggerFactory.getLogger(SiliconflowImageModel.class); + private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class); private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention(); - /** - * The default options used for the image completion requests. - */ - private final SiliconflowImageOptions defaultOptions; + private final SiliconFlowImageOptions defaultOptions; - /** - * The retry template used to retry the OpenAI Image API calls. - */ private final RetryTemplate retryTemplate; - /** - * Low-level access to the OpenAI Image API. - */ - private final SiiconflowmageApi siiconflowmageApi; + private final SiliconFlowImageApi siliconFlowImageApi; - /** - * Observation registry used for instrumentation. - */ private final ObservationRegistry observationRegistry; - /** - * Conventions to use for generating observations. - */ + @Setter private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION; - /** - * Creates an instance of the OpenAiImageModel. - * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with - * the OpenAI Image API. - * @throws IllegalArgumentException if openAiImageApi is null - */ - public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi) { - this(siiconflowmageApi, SiliconflowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) { + this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE); } - /** - * Initializes a new instance of the OpenAiImageModel. - * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with - * the OpenAI Image API. - * @param options The OpenAiImageOptions to configure the image model. - * @param retryTemplate The retry template. - */ - public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi, SiliconflowImageOptions options, RetryTemplate retryTemplate) { - this(siiconflowmageApi, options, retryTemplate, ObservationRegistry.NOOP); + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) { + this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP); } - /** - * Initializes a new instance of the OpenAiImageModel. - * @param siiconflowmageApi The OpenAiImageApi instance to be used for interacting with - * the OpenAI Image API. - * @param options The OpenAiImageOptions to configure the image model. - * @param retryTemplate The retry template. - * @param observationRegistry The ObservationRegistry used for instrumentation. - */ - public SiliconflowImageModel(SiiconflowmageApi siiconflowmageApi, SiliconflowImageOptions options, RetryTemplate retryTemplate, + public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate, ObservationRegistry observationRegistry) { - Assert.notNull(siiconflowmageApi, "OpenAiImageApi must not be null"); + Assert.notNull(siliconFlowImageApi, "OpenAiImageApi must not be null"); Assert.notNull(options, "options must not be null"); Assert.notNull(retryTemplate, "retryTemplate must not be null"); Assert.notNull(observationRegistry, "observationRegistry must not be null"); - this.siiconflowmageApi = siiconflowmageApi; + this.siliconFlowImageApi = siliconFlowImageApi; this.defaultOptions = options; this.retryTemplate = retryTemplate; this.observationRegistry = observationRegistry; @@ -114,11 +82,11 @@ public class SiliconflowImageModel implements ImageModel { @Override public ImageResponse call(ImagePrompt imagePrompt) { - SiiconflowmageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt); + SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt); var observationContext = ImageModelObservationContext.builder() .imagePrompt(imagePrompt) - .provider(OpenAiApiConstants.PROVIDER_NAME) + .provider(SiliconFlowApiConstants.PROVIDER_NAME) .requestOptions(imagePrompt.getOptions()) .build(); @@ -127,7 +95,7 @@ public class SiliconflowImageModel implements ImageModel { this.observationRegistry) .observe(() -> { ResponseEntity imageResponseEntity = this.retryTemplate - .execute(ctx -> this.siiconflowmageApi.createImage(imageRequest)); + .execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest)); ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest); @@ -137,17 +105,17 @@ public class SiliconflowImageModel implements ImageModel { }); } - private SiiconflowmageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt) { + private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt) { String instructions = imagePrompt.getInstructions().get(0).getText(); - SiiconflowmageApi.SiliconflowImageRequest imageRequest = new SiiconflowmageApi.SiliconflowImageRequest(instructions, + SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions, imagePrompt.getOptions().getModel()); - return ModelOptionsUtils.merge(imagePrompt.getOptions(), imageRequest, SiiconflowmageApi.SiliconflowImageRequest.class); + return ModelOptionsUtils.merge(imagePrompt.getOptions(), imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class); } private ImageResponse convertResponse(ResponseEntity imageResponseEntity, - SiiconflowmageApi.SiliconflowImageRequest siliconflowImageRequest) { + SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) { OpenAiImageApi.OpenAiImageResponse imageApiResponse = imageResponseEntity.getBody(); if (imageApiResponse == null) { logger.warn("No image response returned for request: {}", siliconflowImageRequest); diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java similarity index 52% rename from yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java rename to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java index 8af0cbeea8..84f93de06f 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconflowImageOptions.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java @@ -1,17 +1,22 @@ package cn.iocoder.yudao.framework.ai.core.model.siliconflow; import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; import org.springframework.ai.image.ImageOptions; -import org.springframework.ai.openai.OpenAiImageOptions; /** - * 硅基流动画图能力 + * 硅基流动 {@link ImageOptions} * * @author zzt */ @Data -public class SiliconflowImageOptions implements ImageOptions { +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SiliconFlowImageOptions implements ImageOptions { @JsonProperty("model") private String model; @@ -37,7 +42,6 @@ public class SiliconflowImageOptions implements ImageOptions { @JsonProperty("num_inference_steps") private Integer numInferenceSteps = 25; - /** * This value is used to control the degree of match between the generated image and the given prompt. The higher the value, the more the generated image will tend to strictly match the text prompt. The lower the value, the more creative and diverse the generated image will be, potentially containing more unexpected elements. * @@ -47,7 +51,7 @@ public class SiliconflowImageOptions implements ImageOptions { private Float guidanceScale = 0.75F; /** - * 如果想要每次都生成固定的图片,可以把seed设置为固定值。 + * 如果想要每次都生成固定的图片,可以把 seed 设置为固定值 * */ @JsonProperty("seed") @@ -59,13 +63,11 @@ public class SiliconflowImageOptions implements ImageOptions { @JsonProperty("image") private String image; - /** * 宽 */ private Integer width; - /** * 高 */ @@ -85,21 +87,6 @@ public class SiliconflowImageOptions implements ImageOptions { } } - /** - * 硅基流动 - * @return - */ - public static SiliconflowImageOptions.Builder builder() { - return new SiliconflowImageOptions.Builder(); - } - - @Override - public String toString() { - - return "SiliconflowImageOptions{" + "model='" + getModel() + '\'' + ", batch_size=" + batchSize + ", imageSize=" + imageSize + ", negativePrompt='" - + negativePrompt + '\'' + '}'; - } - @Override public Integer getN() { return null; @@ -115,52 +102,4 @@ public class SiliconflowImageOptions implements ImageOptions { return null; } - public static class Builder extends OpenAiImageOptions{ - - private final SiliconflowImageOptions options; - - private Builder() { - this.options = new SiliconflowImageOptions(); - } - - public SiliconflowImageOptions.Builder model(String model) { - this.options.setModel(model); - return this; - } - - public SiliconflowImageOptions.Builder withBatchSize(Integer batchSize) { - options.setBatchSize(batchSize); - return this; - } - - public SiliconflowImageOptions.Builder withModel(String model) { - options.setModel(model); - return this; - } - - public SiliconflowImageOptions.Builder withWidth(Integer width) { - options.setWidth(width); - return this; - } - - public SiliconflowImageOptions.Builder withHeight(Integer height) { - options.setHeight(height); - return this; - } - - public SiliconflowImageOptions.Builder withSeed(Integer seed) { - options.setSeed(seed); - return this; - } - - public SiliconflowImageOptions.Builder withNegativePrompt(String negativePrompt) { - options.setNegativePrompt(negativePrompt); - return this; - } - - public SiliconflowImageOptions build() { - return options; - } - - } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java index 1344a7179f..b6139b4081 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/SiliconFlowChatModelTests.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.framework.ai.chat; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowApiConstants; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowApiConstants; import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -26,11 +26,11 @@ public class SiliconFlowChatModelTests { private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() .openAiApi(OpenAiApi.builder() - .baseUrl(SiiconflowApiConstants.DEFAULT_BASE_URL) + .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL) .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey .build()) .defaultOptions(OpenAiChatOptions.builder() - .model(SiiconflowApiConstants.MODEL_DEFAULT) // 模型 + .model(SiliconFlowApiConstants.MODEL_DEFAULT) // 模型 // .model("deepseek-ai/DeepSeek-R1") // 模型(deepseek-ai/DeepSeek-R1)可用赠费 // .model("Pro/deepseek-ai/DeepSeek-R1") // 模型(Pro/deepseek-ai/DeepSeek-R1)需要付费 .temperature(0.7) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java index 40ad3d3ede..323c4de513 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/image/SiliconFlowImageModelTests.java @@ -1,27 +1,27 @@ package cn.iocoder.yudao.framework.ai.image; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiiconflowmageApi; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageModel; -import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconflowImageOptions; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowImageApi; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowImageModel; +import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowImageOptions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.ai.image.ImagePrompt; import org.springframework.ai.image.ImageResponse; /** - * {@link SiliconflowImageModel} 集成测试 + * {@link SiliconFlowImageModel} 集成测试 */ public class SiliconFlowImageModelTests { - private final SiliconflowImageModel imageModel = new SiliconflowImageModel( - new SiiconflowmageApi("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // 密钥 + private final SiliconFlowImageModel imageModel = new SiliconFlowImageModel( + new SiliconFlowImageApi("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // 密钥 ); @Test @Disabled public void testCall() { // 准备参数 - SiliconflowImageOptions imageOptions = SiliconflowImageOptions.builder() + SiliconFlowImageOptions imageOptions = SiliconFlowImageOptions.builder() .model("Kwai-Kolors/Kolors") .build(); ImagePrompt prompt = new ImagePrompt("万里长城", imageOptions); From ef5e56d5605ca716785bb8c2427aa86ef4e7bd0e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 11:22:46 +0800 Subject: [PATCH 381/386] =?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=A1=85=E5=9F=BA=E6=B5=81?= =?UTF-8?q?=E5=8A=A8=E7=9A=84=E5=9B=BE=E7=89=87=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/core/factory/AiModelFactoryImpl.java | 2 ++ .../siliconflow/SiliconFlowApiConstants.java | 2 ++ .../siliconflow/SiliconFlowImageModel.java | 33 ++++++++++++++++--- .../siliconflow/SiliconFlowImageOptions.java | 4 +-- 4 files changed, 35 insertions(+), 6 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 77ff76486a..3c9f51cf63 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 @@ -207,6 +207,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(QianFanImageModel.class); case ZHI_PU: return SpringUtil.getBean(ZhiPuAiImageModel.class); + case SILICON_FLOW: + return SpringUtil.getBean(SiliconFlowImageModel.class); case OPENAI: return SpringUtil.getBean(OpenAiImageModel.class); case STABLE_DIFFUSION: diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java index 25bf4a1755..4df1b3f3df 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java @@ -27,6 +27,8 @@ public final class SiliconFlowApiConstants { public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B"; + public static final String DEFAULT_IMAGE_MODEL = "Kwai-Kolors/Kolors"; + public static final String PROVIDER_NAME = "Siiconflow"; } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java index 1e3965ed91..e345ebaf8f 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java @@ -31,6 +31,7 @@ import org.springframework.ai.openai.api.OpenAiImageApi; import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata; import org.springframework.ai.retry.RetryUtils; import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; import org.springframework.retry.support.RetryTemplate; import org.springframework.util.Assert; @@ -82,7 +83,8 @@ public class SiliconFlowImageModel implements ImageModel { @Override public ImageResponse call(ImagePrompt imagePrompt) { - SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt); + SiliconFlowImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions); + SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions); var observationContext = ImageModelObservationContext.builder() .imagePrompt(imagePrompt) @@ -105,13 +107,14 @@ public class SiliconFlowImageModel implements ImageModel { }); } - private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt) { + private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt, + SiliconFlowImageOptions requestImageOptions) { String instructions = imagePrompt.getInstructions().get(0).getText(); SiliconFlowImageApi.SiliconflowImageRequest imageRequest = new SiliconFlowImageApi.SiliconflowImageRequest(instructions, - imagePrompt.getOptions().getModel()); + SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL); - return ModelOptionsUtils.merge(imagePrompt.getOptions(), imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class); + return ModelOptionsUtils.merge(requestImageOptions, imageRequest, SiliconFlowImageApi.SiliconflowImageRequest.class); } private ImageResponse convertResponse(ResponseEntity imageResponseEntity, @@ -131,4 +134,26 @@ public class SiliconFlowImageModel implements ImageModel { ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created()); return new ImageResponse(imageGenerationList, openAiImageResponseMetadata); } + + private SiliconFlowImageOptions mergeOptions(@Nullable ImageOptions runtimeOptions, SiliconFlowImageOptions defaultOptions) { + var runtimeOptionsForProvider = ModelOptionsUtils.copyToTarget(runtimeOptions, ImageOptions.class, + SiliconFlowImageOptions.class); + + if (runtimeOptionsForProvider == null) { + return defaultOptions; + } + + return SiliconFlowImageOptions.builder() + // Handle portable image options + .model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel())) + .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN())) + .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth())) + .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight())) + // Handle OpenAI specific image options + .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt())) + .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps())) + .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale())) + .seed(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getSeed(), defaultOptions.getSeed())) + .build(); + } } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java index 84f93de06f..bdd82e9c89 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java @@ -89,12 +89,12 @@ public class SiliconFlowImageOptions implements ImageOptions { @Override public Integer getN() { - return null; + return batchSize; } @Override public String getResponseFormat() { - return null; + return "url"; } @Override From cd4813f7dd78c9ce6bf256bbc351ff622905641f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 12:18:37 +0800 Subject: [PATCH 382/386] =?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=99=BE=E5=B7=9D=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=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/config/YudaoAiAutoConfiguration.java | 28 ++++++++ .../ai/config/YudaoAiProperties.java | 19 ++++++ .../ai/core/enums/AiPlatformEnum.java | 1 + .../ai/core/factory/AiModelFactoryImpl.java | 14 ++++ .../model/baichuan/BaiChuanChatModel.java | 45 ++++++++++++ .../siliconflow/SiliconFlowImageModel.java | 2 +- .../yudao/framework/ai/core/util/AiUtils.java | 1 + .../ai/chat/BaiChuanChatModelTests.java | 68 +++++++++++++++++++ 8 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/baichuan/BaiChuanChatModel.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/BaiChuanChatModelTests.java 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 e014a4cd9f..a454e40e8b 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 @@ -4,6 +4,7 @@ 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.baichuan.BaiChuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; @@ -193,6 +194,33 @@ public class YudaoAiAutoConfiguration { return new XingHuoChatModel(openAiChatModel); } + @Bean + @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true") + public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) { + YudaoAiProperties.BaiChuanProperties properties = yudaoAiProperties.getBaichuan(); + return buildBaiChuanChatClient(properties); + } + + public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuanProperties properties) { + if (StrUtil.isEmpty(properties.getModel())) { + properties.setModel(BaiChuanChatModel.MODEL_DEFAULT); + } + OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(BaiChuanChatModel.BASE_URL) + .apiKey(properties.getApiKey()) + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model(properties.getModel()) + .temperature(properties.getTemperature()) + .maxTokens(properties.getMaxTokens()) + .topP(properties.getTopP()) + .build()) + .toolCallingManager(getToolCallingManager()) + .build(); + return new BaiChuanChatModel(openAiChatModel); + } + @Bean @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true") public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) { 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 296e0af8bc..86d1084ccc 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 @@ -43,6 +43,12 @@ public class YudaoAiProperties { @SuppressWarnings("SpellCheckingInspection") private XingHuoProperties xinghuo; + /** + * 百川 + */ + @SuppressWarnings("SpellCheckingInspection") + private BaiChuanProperties baichuan; + /** * Midjourney 绘图 */ @@ -122,6 +128,19 @@ public class YudaoAiProperties { } + @Data + public static class BaiChuanProperties { + + private String enable; + private String apiKey; + + private String model; + private Double temperature; + private Integer maxTokens; + private Double topP; + + } + @Data public static class MidjourneyProperties { 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 5a8a5c4539..be65f2986f 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 @@ -27,6 +27,7 @@ public enum AiPlatformEnum implements ArrayValuable { SILICON_FLOW("SiliconFlow", "硅基流动"), // 硅基流动 MINI_MAX("MiniMax", "MiniMax"), // 稀宇科技 MOONSHOT("Moonshot", "月之暗灭"), // KIMI + BAI_CHUAN("BaiChuan", "百川智能"), // 百川智能 // ========== 国外平台 ========== 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 3c9f51cf63..6d664eb65f 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 @@ -11,6 +11,7 @@ import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.ai.config.YudaoAiAutoConfiguration; import cn.iocoder.yudao.framework.ai.config.YudaoAiProperties; import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel; import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; import cn.iocoder.yudao.framework.ai.core.model.doubao.DouBaoChatModel; import cn.iocoder.yudao.framework.ai.core.model.hunyuan.HunYuanChatModel; @@ -150,6 +151,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return buildMoonshotChatModel(apiKey, url); case XING_HUO: return buildXingHuoChatModel(apiKey); + case BAI_CHUAN: + return buildBaiChuanChatModel(apiKey); case OPENAI: return buildOpenAiChatModel(apiKey, url); case AZURE_OPENAI: @@ -186,6 +189,8 @@ public class AiModelFactoryImpl implements AiModelFactory { return SpringUtil.getBean(MoonshotChatModel.class); case XING_HUO: return SpringUtil.getBean(XingHuoChatModel.class); + case BAI_CHUAN: + return SpringUtil.getBean(AzureOpenAiChatModel.class); case OPENAI: return SpringUtil.getBean(OpenAiChatModel.class); case AZURE_OPENAI: @@ -441,6 +446,15 @@ public class AiModelFactoryImpl implements AiModelFactory { return new YudaoAiAutoConfiguration().buildXingHuoChatClient(properties); } + /** + * 可参考 {@link YudaoAiAutoConfiguration#baiChuanChatClient(YudaoAiProperties)} + */ + private BaiChuanChatModel buildBaiChuanChatModel(String apiKey) { + YudaoAiProperties.BaiChuanProperties properties = new YudaoAiProperties.BaiChuanProperties() + .setApiKey(apiKey); + return new YudaoAiAutoConfiguration().buildBaiChuanChatClient(properties); + } + /** * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法 */ diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/baichuan/BaiChuanChatModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/baichuan/BaiChuanChatModel.java new file mode 100644 index 0000000000..ac59b70266 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/baichuan/BaiChuanChatModel.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.framework.ai.core.model.baichuan; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import reactor.core.publisher.Flux; + +/** + * 百川 {@link ChatModel} 实现类 + * + * @author 芋道源码 + */ +@Slf4j +@RequiredArgsConstructor +public class BaiChuanChatModel implements ChatModel { + + public static final String BASE_URL = "https://api.baichuan-ai.com"; + + public static final String MODEL_DEFAULT = "Baichuan4-Turbo"; + + /** + * 兼容 OpenAI 接口,进行复用 + */ + private final OpenAiChatModel openAiChatModel; + + @Override + public ChatResponse call(Prompt prompt) { + return openAiChatModel.call(prompt); + } + + @Override + public Flux stream(Prompt prompt) { + return openAiChatModel.stream(prompt); + } + + @Override + public ChatOptions getDefaultOptions() { + return openAiChatModel.getDefaultOptions(); + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java index e345ebaf8f..235699ee66 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java @@ -149,7 +149,7 @@ public class SiliconFlowImageModel implements ImageModel { .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN())) .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth())) .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight())) - // Handle OpenAI specific image options + // Handle SiliconFlow specific image options .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt())) .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps())) .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale())) 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..3b858b4bc5 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 @@ -50,6 +50,7 @@ public class AiUtils { case HUN_YUAN: // 复用 OpenAI 客户端 case XING_HUO: // 复用 OpenAI 客户端 case SILICON_FLOW: // 复用 OpenAI 客户端 + case BAI_CHUAN: // 复用 OpenAI 客户端 return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) .toolNames(toolNames).build(); case AZURE_OPENAI: diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/BaiChuanChatModelTests.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/BaiChuanChatModelTests.java new file mode 100644 index 0000000000..9ae36dbb87 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/BaiChuanChatModelTests.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.framework.ai.chat; + +import cn.iocoder.yudao.framework.ai.core.model.baichuan.BaiChuanChatModel; +import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; +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.OpenAiChatOptions; +import org.springframework.ai.openai.api.OpenAiApi; +import reactor.core.publisher.Flux; + +import java.util.ArrayList; +import java.util.List; + +/** + * {@link BaiChuanChatModel} 集成测试 + * + * @author 芋道源码 + */ +public class BaiChuanChatModelTests { + + private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() + .openAiApi(OpenAiApi.builder() + .baseUrl(BaiChuanChatModel.BASE_URL) + .apiKey("sk-61b6766a94c70786ed02673f5e16af3c") // apiKey + .build()) + .defaultOptions(OpenAiChatOptions.builder() + .model("Baichuan4-Turbo") // 模型(https://platform.baichuan-ai.com/docs/api) + .temperature(0.7) + .build()) + .build(); + + private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel); + + @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); + } + + @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(System.out::println).then().block(); + } + +} From 138239324c6b30bf36788f9112cf78c37e4fb00e Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 23 Mar 2025 12:48:13 +0800 Subject: [PATCH 383/386] =?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=99=BE=E5=B7=9D=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=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-server/src/main/resources/application.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml index 13683df543..57b3b87d0a 100644 --- a/yudao-server/src/main/resources/application.yaml +++ b/yudao-server/src/main/resources/application.yaml @@ -212,6 +212,10 @@ yudao: appKey: 75b161ed2aef4719b275d6e7f2a4d4cd secretKey: YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz model: generalv3.5 + baichuan: # 百川智能 + enable: true + api-key: sk-abc + model: Baichuan4-Turbo midjourney: enable: true # base-url: https://api.holdai.top/mj-relax/mj From 587504c36ac3467df9ff9672045640e245a849f8 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Mon, 24 Mar 2025 15:15:27 +0800 Subject: [PATCH 384/386] =?UTF-8?q?feat:=20AI=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/ai/enums/ErrorCodeConstants.java | 4 + .../admin/workflow/AiWorkflowController.java | 85 ++++++++++ .../workflow/vo/AiWorkflowPageReqVO.java | 26 +++ .../admin/workflow/vo/AiWorkflowRespVO.java | 27 +++ .../workflow/vo/AiWorkflowSaveReqVO.java | 22 +++ .../workflow/vo/AiWorkflowTestReqVO.java | 20 +++ .../vo/AiWorkflowUpdateModelReqVO.java | 18 ++ .../dal/dataobject/workflow/AiWorkflowDO.java | 40 +++++ .../dal/mysql/workflow/AiWorkflowMapper.java | 28 ++++ .../service/workflow/AiWorkflowService.java | 69 ++++++++ .../workflow/AiWorkflowServiceImpl.java | 157 ++++++++++++++++++ .../yudao-spring-boot-starter-ai/pom.xml | 8 + 12 files changed, 504 insertions(+) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.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 8b235f9ac5..fa072f3fcb 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 @@ -61,4 +61,8 @@ public interface ErrorCodeConstants { ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在"); ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean"); + // ========== AI 工作流 1-040-011-000 ========== + ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, "工作流不存在"); + ErrorCode WORKFLOW_KEY_EXISTS = new ErrorCode(1_040_011_001, "工作流标识已存在"); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java new file mode 100644 index 0000000000..1270f7679c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.ai.controller.admin.workflow; + +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.workflow.vo.*; +import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO; +import cn.iocoder.yudao.module.ai.service.workflow.AiWorkflowService; +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 lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - AI 工作流") +@RestController +@RequestMapping("/ai/workflow") +@Slf4j +public class AiWorkflowController { + + @Resource + private AiWorkflowService workflowService; + + @PostMapping("/create") + @Operation(summary = "创建 AI 工作流") + @PreAuthorize("@ss.hasPermission('ai:workflow:create')") + public CommonResult createWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO createReqVO) { + return success(workflowService.createWorkflow(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新 AI 工作流") + @PreAuthorize("@ss.hasPermission('ai:workflow:update')") + public CommonResult updateWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO updateReqVO) { + workflowService.updateWorkflow(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除 AI 工作流") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:workflow:delete')") + public CommonResult deleteWorkflow(@RequestParam("id") Long id) { + workflowService.deleteWorkflow(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得 AI 工作流") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:workflow:query')") + public CommonResult getWorkflow(@RequestParam("id") Long id) { + AiWorkflowDO workflow = workflowService.getWorkflow(id); + return success(BeanUtils.toBean(workflow, AiWorkflowRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得 AI 工作流分页") + @PreAuthorize("@ss.hasPermission('ai:workflow:query')") + public CommonResult> getWorkflowPage(@Valid AiWorkflowPageReqVO pageReqVO) { + PageResult pageResult = workflowService.getWorkflowPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiWorkflowRespVO.class)); + } + + @PutMapping("/updateWorkflowModel") + @Operation(summary = "更新 AI 工作流模型") + @PreAuthorize("@ss.hasPermission('ai:workflow:update')") + public CommonResult updateWorkflowModel(@Valid @RequestBody AiWorkflowUpdateModelReqVO updateReqVO) { + workflowService.updateWorkflowModel(updateReqVO); + return success(true); + } + + @PostMapping("/test") + @Operation(summary = "测试 AI 工作流") + @PreAuthorize("@ss.hasPermission('ai:workflow:test')") + public CommonResult testWorkflow(@Valid @RequestBody AiWorkflowTestReqVO testReqVO) { + return success(workflowService.testWorkflow(testReqVO)); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java new file mode 100644 index 0000000000..5418057817 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +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 AiWorkflowPageReqVO extends PageParam { + + @Schema(description = "名称", example = "工作流") + private String name; + + @Schema(description = "标识", example = "FLOW") + private String definitionKey; + + @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/workflow/vo/AiWorkflowRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java new file mode 100644 index 0000000000..b1c66b91de --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - AI 工作流 Response VO") +@Data +public class AiWorkflowRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW") + private String definitionKey; + + @Schema(description = "工作流名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流") + private String name; + + @Schema(description = "工作流模型 JSON", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private String model; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java new file mode 100644 index 0000000000..ec269cc034 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - AI 工作流新增/修改 Request VO") +@Data +public class AiWorkflowSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW") + @NotEmpty(message = "工作流标识不能为空") + private String definitionKey; + + @Schema(description = "工作流名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流") + @NotEmpty(message = "工作流名称不能为空") + private String name; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java new file mode 100644 index 0000000000..077a3020a6 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Map; + +@Schema(description = "管理后台 - AI 工作流测试 Request VO") +@Data +public class AiWorkflowTestReqVO { + + @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + @NotEmpty(message = "工作流模型不能为空") + private String model; + + @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private Map params; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java new file mode 100644 index 0000000000..00a279ba54 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - AI 工作流修改流程模型 Request VO") +@Data +public class AiWorkflowUpdateModelReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + @NotEmpty(message = "工作流模型不能为空") + private String model; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java new file mode 100644 index 0000000000..db6c090467 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.ai.dal.dataobject.workflow; + +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.Data; + +/** + * AI 工作流 DO + * + * @author lesan + */ +@TableName(value = "ai_workflow", autoResultMap = true) +@KeySequence("ai_workflow") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +public class AiWorkflowDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + + /** + * 工作流标识 + */ + private String definitionKey; + + /** + * 工作流名称 + */ + private String name; + + /** + * 工作流模型 JSON 数据 + */ + private String model; + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java new file mode 100644 index 0000000000..f6be0cec7b --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.workflow; + +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.workflow.vo.AiWorkflowPageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI 工作流 Mapper + * + * @author lesan + */ +@Mapper +public interface AiWorkflowMapper extends BaseMapperX { + + default AiWorkflowDO selectByKey(String key) { + return selectOne(AiWorkflowDO::getDefinitionKey, key); + } + + default PageResult selectPage(AiWorkflowPageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiWorkflowDO::getName, pageReqVO.getName()) + .likeIfPresent(AiWorkflowDO::getDefinitionKey, pageReqVO.getDefinitionKey())); + } + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java new file mode 100644 index 0000000000..31fd476540 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.ai.service.workflow; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowUpdateModelReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO; +import jakarta.validation.Valid; + +/** + * AI 工作流 Service 接口 + * + * @author lesan + */ +public interface AiWorkflowService { + + /** + * 创建 AI 工作流 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createWorkflow(@Valid AiWorkflowSaveReqVO createReqVO); + + /** + * 更新 AI 工作流 + * + * @param updateReqVO 更新信息 + */ + void updateWorkflow(@Valid AiWorkflowSaveReqVO updateReqVO); + + /** + * 删除 AI 工作流 + * + * @param id 编号 + */ + void deleteWorkflow(Long id); + + /** + * 获得 AI 工作流 + * + * @param id 编号 + * @return AI 工作流 + */ + AiWorkflowDO getWorkflow(Long id); + + /** + * 获得 AI 工作流分页 + * + * @param pageReqVO 分页查询 + * @return AI 工作流分页 + */ + PageResult getWorkflowPage(AiWorkflowPageReqVO pageReqVO); + + /** + * 修改工作流模型 JSON 数据 + * + * @param updateReqVO 更新数据 + */ + void updateWorkflowModel(AiWorkflowUpdateModelReqVO updateReqVO); + + /** + * 测试 AI 工作流 + * + * @param testReqVO 测试数据 + */ + Object testWorkflow(AiWorkflowTestReqVO testReqVO); +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java new file mode 100644 index 0000000000..53128b7c2a --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java @@ -0,0 +1,157 @@ +package cn.iocoder.yudao.module.ai.service.workflow; + +import cn.hutool.core.util.ObjUtil; +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.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowUpdateModelReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO; +import cn.iocoder.yudao.module.ai.dal.mysql.workflow.AiWorkflowMapper; +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import dev.tinyflow.core.Tinyflow; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_KEY_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_NOT_EXISTS; + +/** + * AI 工作流 Service 实现类 + * + * @author lesan + */ +@Service +@Slf4j +public class AiWorkflowServiceImpl implements AiWorkflowService { + + @Resource + private AiWorkflowMapper workflowMapper; + + @Resource + private AiApiKeyService apiKeyService; + + @Override + public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) { + validateWorkflowForCreateOrUpdate(null, createReqVO.getDefinitionKey()); + AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class); + workflow.setModel(StrUtil.EMPTY); + workflowMapper.insert(workflow); + return workflow.getId(); + } + + @Override + public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) { + validateWorkflowForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getDefinitionKey()); + AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class); + workflowMapper.updateById(workflow); + } + + @Override + public void deleteWorkflow(Long id) { + validateWorkflowExists(id); + workflowMapper.deleteById(id); + } + + @Override + public AiWorkflowDO getWorkflow(Long id) { + return workflowMapper.selectById(id); + } + + @Override + public PageResult getWorkflowPage(AiWorkflowPageReqVO pageReqVO) { + return workflowMapper.selectPage(pageReqVO); + } + + @Override + public void updateWorkflowModel(AiWorkflowUpdateModelReqVO updateReqVO) { + validateWorkflowExists(updateReqVO.getId()); + workflowMapper.updateById(new AiWorkflowDO().setId(updateReqVO.getId()).setModel(updateReqVO.getModel())); + } + + @Override + public Object testWorkflow(AiWorkflowTestReqVO testReqVO) { + Map variables = testReqVO.getParams(); + Tinyflow tinyflow = parseFlowParam(testReqVO.getModel()); + return tinyflow.toChain().executeForResult(variables); + } + + private void validateWorkflowForCreateOrUpdate(Long id, String key) { + validateWorkflowExists(id); + validateKeyUnique(id, key); + } + + private void validateWorkflowExists(Long id) { + if (ObjUtil.isNull(id)) { + return; + } + AiWorkflowDO workflow = workflowMapper.selectById(id); + if (ObjUtil.isNull(workflow)) { + throw exception(WORKFLOW_NOT_EXISTS); + } + } + + private void validateKeyUnique(Long id, String key) { + if (StrUtil.isBlank(key)) { + return; + } + AiWorkflowDO workflow = workflowMapper.selectByKey(key); + if (ObjUtil.isNull(workflow)) { + return; + } + if (ObjUtil.isNull(id)) { + throw exception(WORKFLOW_KEY_EXISTS); + } + if (ObjUtil.notEqual(workflow.getId(), id)) { + throw exception(WORKFLOW_KEY_EXISTS); + } + } + + private Tinyflow parseFlowParam(String model) { + JSONObject json = JSONObject.parseObject(model); + JSONArray nodeArr = json.getJSONArray("nodes"); + Tinyflow tinyflow = new Tinyflow(json.toJSONString()); + for (int i = 0; i < nodeArr.size(); i++) { + JSONObject node = nodeArr.getJSONObject(i); + switch (node.getString("type")) { + case "llmNode": + JSONObject data = node.getJSONObject("data"); + AiApiKeyDO apiKey = apiKeyService.getApiKey(data.getLong("llmId")); + switch (apiKey.getPlatform()) { + // TODO @lesan 需要讨论一下这里怎么弄 + case "OpenAI": + break; + case "Ollama": + break; + case "YiYan": + break; + case "XingHuo": + break; + case "TongYi": + break; + case "DeepSeek": + break; + case "ZhiPu": + break; + } + break; + case "internalNode": + break; + default: + break; + } + } + return tinyflow; + } + + +} 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 f37f3709c6..5740cf2041 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -15,6 +15,7 @@ AI 大模型拓展,接入国内外大模型 1.0.0-M6 + 1.0.0-rc.3 @@ -117,6 +118,13 @@ + + + dev.tinyflow + tinyflow-java-core + ${tinyflow.version} + + org.springframework.boot From 4bb52bb37d1df17e75ca528594003aba00926172 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 25 Mar 2025 09:55:05 +0800 Subject: [PATCH 385/386] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?= =?UTF-8?q?=E5=AE=A1=E3=80=91AI=EF=BC=9A=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/workflow/AiWorkflowController.java | 1 + .../admin/workflow/vo/AiWorkflowSaveReqVO.java | 2 +- .../ai/dal/dataobject/workflow/AiWorkflowDO.java | 12 ++++++------ .../ai/service/workflow/AiWorkflowService.java | 1 + .../ai/service/workflow/AiWorkflowServiceImpl.java | 3 ++- yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml | 2 +- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java index 1270f7679c..44e60a0e98 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java @@ -67,6 +67,7 @@ public class AiWorkflowController { return success(BeanUtils.toBean(pageResult, AiWorkflowRespVO.class)); } + // TODO @lesan:要不融合到 updateWorkflow 接口? @PutMapping("/updateWorkflowModel") @Operation(summary = "更新 AI 工作流模型") @PreAuthorize("@ss.hasPermission('ai:workflow:update')") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java index ec269cc034..5c4ebc4a4b 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java @@ -8,7 +8,7 @@ import lombok.Data; @Data public class AiWorkflowSaveReqVO { - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @Schema(description = "编号", example = "1") private Long id; @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java index db6c090467..ef05f7ea17 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java @@ -21,17 +21,17 @@ public class AiWorkflowDO extends BaseDO { */ @TableId private Long id; - - /** - * 工作流标识 - */ - private String definitionKey; - /** * 工作流名称 */ private String name; + /** + * 工作流标识 + */ + // TODO @lesan:要不换成 code?主要想,和 bpm 工作流,有点区分,字段上。 + private String definitionKey; + // TODO @lesan:graph 用这个如何?发现大家貌似更爱用这个字段哈。图! /** * 工作流模型 JSON 数据 */ diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java index 31fd476540..bbc167d486 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java @@ -66,4 +66,5 @@ public interface AiWorkflowService { * @param testReqVO 测试数据 */ Object testWorkflow(AiWorkflowTestReqVO testReqVO); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java index 53128b7c2a..0bf299da49 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java @@ -117,6 +117,7 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { } private Tinyflow parseFlowParam(String model) { + // TODO @lesan:可以使用 jackson 哇? JSONObject json = JSONObject.parseObject(model); JSONArray nodeArr = json.getJSONArray("nodes"); Tinyflow tinyflow = new Tinyflow(json.toJSONString()); @@ -128,6 +129,7 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { AiApiKeyDO apiKey = apiKeyService.getApiKey(data.getLong("llmId")); switch (apiKey.getPlatform()) { // TODO @lesan 需要讨论一下这里怎么弄 + // TODO @lesan llmId 对应 model 的编号如何?这样的话,就是 apiModelService 提供一个获取 LLM 的方法。然后,创建的方法,也在 AiModelFactory 提供。可以先接个 deepseek 先。deepseek yyds! case "OpenAI": break; case "Ollama": @@ -153,5 +155,4 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { return tinyflow; } - } 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 5740cf2041..3ed8724b76 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml @@ -118,8 +118,8 @@ + - dev.tinyflow tinyflow-java-core ${tinyflow.version} From 2aff972600005080775f2d3df10763a0fb83e001 Mon Sep 17 00:00:00 2001 From: Lesan <1960681385@qq.com> Date: Fri, 28 Mar 2025 15:54:46 +0800 Subject: [PATCH 386/386] =?UTF-8?q?feat:=20AI=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?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/ai/enums/ErrorCodeConstants.java | 2 +- .../admin/workflow/AiWorkflowController.java | 9 ----- .../workflow/vo/AiWorkflowPageReqVO.java | 8 ++++- .../admin/workflow/vo/AiWorkflowRespVO.java | 10 ++++-- .../workflow/vo/AiWorkflowSaveReqVO.java | 14 +++++++- .../workflow/vo/AiWorkflowTestReqVO.java | 2 +- .../vo/AiWorkflowUpdateModelReqVO.java | 18 ---------- .../dal/dataobject/workflow/AiWorkflowDO.java | 19 +++++++--- .../dal/mysql/workflow/AiWorkflowMapper.java | 8 +++-- .../service/workflow/AiWorkflowService.java | 8 ----- .../workflow/AiWorkflowServiceImpl.java | 36 ++++++++----------- 11 files changed, 64 insertions(+), 70 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.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 fa072f3fcb..8a8a388324 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 @@ -63,6 +63,6 @@ public interface ErrorCodeConstants { // ========== AI 工作流 1-040-011-000 ========== ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, "工作流不存在"); - ErrorCode WORKFLOW_KEY_EXISTS = new ErrorCode(1_040_011_001, "工作流标识已存在"); + ErrorCode WORKFLOW_CODE_EXISTS = new ErrorCode(1_040_011_001, "工作流标识已存在"); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java index 44e60a0e98..d558d90454 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java @@ -67,15 +67,6 @@ public class AiWorkflowController { return success(BeanUtils.toBean(pageResult, AiWorkflowRespVO.class)); } - // TODO @lesan:要不融合到 updateWorkflow 接口? - @PutMapping("/updateWorkflowModel") - @Operation(summary = "更新 AI 工作流模型") - @PreAuthorize("@ss.hasPermission('ai:workflow:update')") - public CommonResult updateWorkflowModel(@Valid @RequestBody AiWorkflowUpdateModelReqVO updateReqVO) { - workflowService.updateWorkflowModel(updateReqVO); - return success(true); - } - @PostMapping("/test") @Operation(summary = "测试 AI 工作流") @PreAuthorize("@ss.hasPermission('ai:workflow:test')") diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java index 5418057817..e55b85ea90 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; +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; @@ -17,7 +19,11 @@ public class AiWorkflowPageReqVO extends PageParam { private String name; @Schema(description = "标识", example = "FLOW") - private String definitionKey; + private String code; + + @Schema(description = "状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java index b1c66b91de..e3a28ad648 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java @@ -13,13 +13,19 @@ public class AiWorkflowRespVO { private Long id; @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW") - private String definitionKey; + private String code; @Schema(description = "工作流名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流") private String name; + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流") + private String remark; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + @Schema(description = "工作流模型 JSON", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private String model; + private String graph; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") private LocalDateTime createTime; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java index 5c4ebc4a4b..0a63c37732 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; @Schema(description = "管理后台 - AI 工作流新增/修改 Request VO") @@ -13,10 +14,21 @@ public class AiWorkflowSaveReqVO { @Schema(description = "工作流标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW") @NotEmpty(message = "工作流标识不能为空") - private String definitionKey; + private String code; @Schema(description = "工作流名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "工作流") @NotEmpty(message = "工作流名称不能为空") private String name; + @Schema(description = "备注", example = "FLOW") + private String remark; + + @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + @NotEmpty(message = "工作流模型不能为空") + private String graph; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW") + @NotNull(message = "状态不能为空") + private Integer status; + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java index 077a3020a6..4dc509e89d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java @@ -12,7 +12,7 @@ public class AiWorkflowTestReqVO { @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") @NotEmpty(message = "工作流模型不能为空") - private String model; + private String graph; @Schema(description = "参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") private Map params; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java deleted file mode 100644 index 00a279ba54..0000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowUpdateModelReqVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - AI 工作流修改流程模型 Request VO") -@Data -public class AiWorkflowUpdateModelReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "工作流模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - @NotEmpty(message = "工作流模型不能为空") - private String model; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java index ef05f7ea17..d844f7da2e 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.workflow; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; @@ -28,13 +29,23 @@ public class AiWorkflowDO extends BaseDO { /** * 工作流标识 */ - // TODO @lesan:要不换成 code?主要想,和 bpm 工作流,有点区分,字段上。 - private String definitionKey; + private String code; - // TODO @lesan:graph 用这个如何?发现大家貌似更爱用这个字段哈。图! /** * 工作流模型 JSON 数据 */ - private String model; + private String graph; + + /** + * 备注 + */ + private String remark; + + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java index f6be0cec7b..3770dbf0b5 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java @@ -15,14 +15,16 @@ import org.apache.ibatis.annotations.Mapper; @Mapper public interface AiWorkflowMapper extends BaseMapperX { - default AiWorkflowDO selectByKey(String key) { - return selectOne(AiWorkflowDO::getDefinitionKey, key); + default AiWorkflowDO selectByCode(String code) { + return selectOne(AiWorkflowDO::getCode, code); } default PageResult selectPage(AiWorkflowPageReqVO pageReqVO) { return selectPage(pageReqVO, new LambdaQueryWrapperX() + .eqIfPresent(AiWorkflowDO::getStatus, pageReqVO.getStatus()) .likeIfPresent(AiWorkflowDO::getName, pageReqVO.getName()) - .likeIfPresent(AiWorkflowDO::getDefinitionKey, pageReqVO.getDefinitionKey())); + .likeIfPresent(AiWorkflowDO::getCode, pageReqVO.getCode()) + .betweenIfPresent(AiWorkflowDO::getCreateTime, pageReqVO.getCreateTime())); } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java index bbc167d486..51a3aea751 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java @@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO; import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowUpdateModelReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO; import jakarta.validation.Valid; @@ -53,13 +52,6 @@ public interface AiWorkflowService { */ PageResult getWorkflowPage(AiWorkflowPageReqVO pageReqVO); - /** - * 修改工作流模型 JSON 数据 - * - * @param updateReqVO 更新数据 - */ - void updateWorkflowModel(AiWorkflowUpdateModelReqVO updateReqVO); - /** * 测试 AI 工作流 * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java index 0bf299da49..70d28496c8 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO; import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO; import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowUpdateModelReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO; import cn.iocoder.yudao.module.ai.dal.mysql.workflow.AiWorkflowMapper; @@ -22,7 +21,7 @@ import org.springframework.stereotype.Service; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_KEY_EXISTS; +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_CODE_EXISTS; import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_NOT_EXISTS; /** @@ -42,16 +41,15 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { @Override public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) { - validateWorkflowForCreateOrUpdate(null, createReqVO.getDefinitionKey()); + validateWorkflowForCreateOrUpdate(null, createReqVO.getCode()); AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class); - workflow.setModel(StrUtil.EMPTY); workflowMapper.insert(workflow); return workflow.getId(); } @Override public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) { - validateWorkflowForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getDefinitionKey()); + validateWorkflowForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getCode()); AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class); workflowMapper.updateById(workflow); } @@ -72,22 +70,16 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { return workflowMapper.selectPage(pageReqVO); } - @Override - public void updateWorkflowModel(AiWorkflowUpdateModelReqVO updateReqVO) { - validateWorkflowExists(updateReqVO.getId()); - workflowMapper.updateById(new AiWorkflowDO().setId(updateReqVO.getId()).setModel(updateReqVO.getModel())); - } - @Override public Object testWorkflow(AiWorkflowTestReqVO testReqVO) { Map variables = testReqVO.getParams(); - Tinyflow tinyflow = parseFlowParam(testReqVO.getModel()); + Tinyflow tinyflow = parseFlowParam(testReqVO.getGraph()); return tinyflow.toChain().executeForResult(variables); } - private void validateWorkflowForCreateOrUpdate(Long id, String key) { + private void validateWorkflowForCreateOrUpdate(Long id, String code) { validateWorkflowExists(id); - validateKeyUnique(id, key); + validateCodeUnique(id, code); } private void validateWorkflowExists(Long id) { @@ -100,27 +92,27 @@ public class AiWorkflowServiceImpl implements AiWorkflowService { } } - private void validateKeyUnique(Long id, String key) { - if (StrUtil.isBlank(key)) { + private void validateCodeUnique(Long id, String code) { + if (StrUtil.isBlank(code)) { return; } - AiWorkflowDO workflow = workflowMapper.selectByKey(key); + AiWorkflowDO workflow = workflowMapper.selectByCode(code); if (ObjUtil.isNull(workflow)) { return; } if (ObjUtil.isNull(id)) { - throw exception(WORKFLOW_KEY_EXISTS); + throw exception(WORKFLOW_CODE_EXISTS); } if (ObjUtil.notEqual(workflow.getId(), id)) { - throw exception(WORKFLOW_KEY_EXISTS); + throw exception(WORKFLOW_CODE_EXISTS); } } - private Tinyflow parseFlowParam(String model) { + private Tinyflow parseFlowParam(String graph) { // TODO @lesan:可以使用 jackson 哇? - JSONObject json = JSONObject.parseObject(model); + JSONObject json = JSONObject.parseObject(graph); JSONArray nodeArr = json.getJSONArray("nodes"); - Tinyflow tinyflow = new Tinyflow(json.toJSONString()); + Tinyflow tinyflow = new Tinyflow(json.toJSONString()); for (int i = 0; i < nodeArr.size(); i++) { JSONObject node = nodeArr.getJSONObject(i); switch (node.getString("type")) {