From e26903b06ba68e56712e4c65a9f05128b0fd9cf2 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, 23 Mar 2025 21:55:08 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=8A=9F=E8=83=BD=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E3=80=91IoT:=20=E5=A2=9E=E5=8A=A0=E4=BA=A7=E5=93=81=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E6=8B=AC=E5=A2=9E=E5=88=A0=E6=94=B9=E6=9F=A5=E5=8F=8A=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/iot/enums/ErrorCodeConstants.java | 3 + yudao-module-iot/yudao-module-iot-biz/pom.xml | 9 +- .../product/IotProductScriptController.java | 99 ++++++++ .../vo/script/IotProductScriptPageReqVO.java | 46 ++++ .../vo/script/IotProductScriptRespVO.java | 63 +++++ .../vo/script/IotProductScriptSaveReqVO.java | 42 ++++ .../vo/script/IotProductScriptTestReqVO.java | 35 +++ .../vo/script/IotProductScriptTestRespVO.java | 39 ++++ .../IotProductScriptUpdateStatusReqVO.java | 19 ++ .../product/IotProductScriptDO.java | 72 ++++++ .../mysql/product/IotProductScriptMapper.java | 31 +++ .../product/IotProductScriptService.java | 82 +++++++ .../product/IotProductScriptServiceImpl.java | 219 ++++++++++++++++++ 13 files changed, 758 insertions(+), 1 deletion(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptPageReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptSaveReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestRespVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductScriptMapper.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptService.java create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.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 c719aeaa28..a19b800061 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 @@ -75,4 +75,7 @@ public interface ErrorCodeConstants { // ========== IoT 规则场景(场景联动) 1-050-011-000 ========== ErrorCode RULE_SCENE_NOT_EXISTS = new ErrorCode(1_050_011_000, "IoT 规则场景(场景联动)不存在"); + // ========== IoT 产品脚本信息 1-050-012-000 ========== + ErrorCode PRODUCT_SCRIPT_NOT_EXISTS = new ErrorCode(1_050_012_000, "IoT 产品脚本信息不存在"); + } \ 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 8721e4de93..c5a968207f 100644 --- a/yudao-module-iot/yudao-module-iot-biz/pom.xml +++ b/yudao-module-iot/yudao-module-iot-biz/pom.xml @@ -69,6 +69,13 @@ yudao-spring-boot-starter-excel + + + cn.iocoder.boot + yudao-module-iot-plugin-script + ${revision} + + org.apache.rocketmq @@ -87,7 +94,7 @@ - org.pf4j + org.pf4j pf4j-spring diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java new file mode 100644 index 0000000000..7e95ea2e0e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductScriptController.java @@ -0,0 +1,99 @@ +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.script.*; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO; +import cn.iocoder.yudao.module.iot.service.product.IotProductScriptService; +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-script") +@Validated +public class IotProductScriptController { + + @Resource + private IotProductScriptService productScriptService; + + @PostMapping("/create") + @Operation(summary = "创建产品脚本") + @PreAuthorize("@ss.hasPermission('iot:product-script:create')") + public CommonResult createProductScript(@Valid @RequestBody IotProductScriptSaveReqVO createReqVO) { + return success(productScriptService.createProductScript(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新产品脚本") + @PreAuthorize("@ss.hasPermission('iot:product-script:update')") + public CommonResult updateProductScript(@Valid @RequestBody IotProductScriptSaveReqVO updateReqVO) { + productScriptService.updateProductScript(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除产品脚本") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:product-script:delete')") + public CommonResult deleteProductScript(@RequestParam("id") Long id) { + productScriptService.deleteProductScript(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得产品脚本详情") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:product-script:query')") + public CommonResult getProductScript(@RequestParam("id") Long id) { + IotProductScriptDO productScript = productScriptService.getProductScript(id); + return success(BeanUtils.toBean(productScript, IotProductScriptRespVO.class)); + } + + @GetMapping("/list-by-product") + @Operation(summary = "获得产品的脚本列表") + @Parameter(name = "productId", description = "产品编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:product-script:query')") + public CommonResult> getProductScriptListByProductId( + @RequestParam("productId") Long productId) { + List list = productScriptService.getProductScriptListByProductId(productId); + return success(BeanUtils.toBean(list, IotProductScriptRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得产品脚本分页") + @PreAuthorize("@ss.hasPermission('iot:product-script:query')") + public CommonResult> getProductScriptPage( + @Valid IotProductScriptPageReqVO pageReqVO) { + PageResult pageResult = productScriptService.getProductScriptPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, IotProductScriptRespVO.class)); + } + + @PostMapping("/test") + @Operation(summary = "测试产品脚本") + @PreAuthorize("@ss.hasPermission('iot:product-script:test')") + public CommonResult testProductScript( + @Valid @RequestBody IotProductScriptTestReqVO testReqVO) { + return success(productScriptService.testProductScript(testReqVO)); + } + + @PutMapping("/update-status") + @Operation(summary = "更新产品脚本状态") + @PreAuthorize("@ss.hasPermission('iot:product-script:update')") + public CommonResult updateProductScriptStatus( + @Valid @RequestBody IotProductScriptUpdateStatusReqVO updateStatusReqVO) { + productScriptService.updateProductScriptStatus(updateStatusReqVO.getId(), updateStatusReqVO.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/product/vo/script/IotProductScriptPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptPageReqVO.java new file mode 100644 index 0000000000..73df10a617 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptPageReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script; + +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 IotProductScriptPageReqVO extends PageParam { + + @Schema(description = "产品ID", example = "28277") + private Long productId; + + @Schema(description = "产品唯一标识符") + private String productKey; + + @Schema(description = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)", example = "2") + private String scriptType; + + @Schema(description = "脚本语言") + private String scriptLanguage; + + @Schema(description = "状态(0=禁用 1=启用)", example = "2") + private Integer status; + + @Schema(description = "备注说明", example = "你说的对") + private String remark; + + @Schema(description = "最后测试时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] lastTestTime; + + @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/script/IotProductScriptRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptRespVO.java new file mode 100644 index 0000000000..1530b1f07d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptRespVO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script; + +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 IotProductScriptRespVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "26565") + @ExcelProperty("主键") + private Long id; + + @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28277") + @ExcelProperty("产品ID") + private Long productId; + + @Schema(description = "产品唯一标识符", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("产品唯一标识符") + private String productKey; + + @Schema(description = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)") + private String scriptType; + + @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("脚本内容") + private String scriptContent; + + @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("脚本语言") + private String scriptLanguage; + + @Schema(description = "状态(0=禁用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @ExcelProperty("状态(0=禁用 1=启用)") + private Integer status; + + @Schema(description = "备注说明", example = "你说的对") + @ExcelProperty("备注说明") + private String remark; + + @Schema(description = "最后测试时间") + @ExcelProperty("最后测试时间") + private LocalDateTime lastTestTime; + + @Schema(description = "最后测试结果(0=失败 1=成功)") + @ExcelProperty("最后测试结果(0=失败 1=成功)") + private Integer lastTestResult; + + @Schema(description = "脚本版本号", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("脚本版本号") + private Integer version; + + @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/script/IotProductScriptSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptSaveReqVO.java new file mode 100644 index 0000000000..05d685b4ac --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptSaveReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script; + +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 IotProductScriptSaveReqVO { + + @Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "26565") + private Long id; + + @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "28277") + @NotNull(message = "产品ID不能为空") + private Long productId; + + @Schema(description = "产品唯一标识符", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "产品唯一标识符不能为空") + private String productKey; + + @Schema(description = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotEmpty(message = "脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码)不能为空") + private String scriptType; + + @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "脚本内容不能为空") + private String scriptContent; + + @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "脚本语言不能为空") + private String scriptLanguage; + + @Schema(description = "状态(0=禁用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "状态(0=禁用 1=启用)不能为空") + private Integer status; + + @Schema(description = "备注说明", example = "你说的对") + private String remark; + +} \ 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/script/IotProductScriptTestReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestReqVO.java new file mode 100644 index 0000000000..e571b7c044 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script; + +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 IotProductScriptTestReqVO { + + @Schema(description = "脚本ID,如果已保存脚本则传入", example = "1024") + private Long id; + + @Schema(description = "产品ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "产品ID不能为空") + private Long productId; + + @Schema(description = "脚本类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "property_parser") + @NotEmpty(message = "脚本类型不能为空") + private String scriptType; + + @Schema(description = "脚本内容", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "脚本内容不能为空") + private String scriptContent; + + @Schema(description = "脚本语言", requiredMode = Schema.RequiredMode.REQUIRED, example = "javascript") + @NotEmpty(message = "脚本语言不能为空") + private String scriptLanguage; + + @Schema(description = "测试输入数据", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "测试输入数据不能为空") + private String testInput; + +} \ 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/script/IotProductScriptTestRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestRespVO.java new file mode 100644 index 0000000000..3dec9f6988 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptTestRespVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 产品脚本测试 Response VO") +@Data +public class IotProductScriptTestRespVO { + + @Schema(description = "测试是否成功", requiredMode = Schema.RequiredMode.REQUIRED) + private Boolean success; + + @Schema(description = "测试结果输出") + private Object output; + + @Schema(description = "错误消息,失败时返回") + private String errorMessage; + + @Schema(description = "执行耗时(毫秒)") + private Long executionTimeMs; + + // 静态工厂方法 - 成功 + public static IotProductScriptTestRespVO success(Object output, Long executionTimeMs) { + IotProductScriptTestRespVO respVO = new IotProductScriptTestRespVO(); + respVO.setSuccess(true); + respVO.setOutput(output); + respVO.setExecutionTimeMs(executionTimeMs); + return respVO; + } + + // 静态工厂方法 - 失败 + public static IotProductScriptTestRespVO error(String errorMessage, Long executionTimeMs) { + IotProductScriptTestRespVO respVO = new IotProductScriptTestRespVO(); + respVO.setSuccess(false); + respVO.setErrorMessage(errorMessage); + respVO.setExecutionTimeMs(executionTimeMs); + 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/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java new file mode 100644 index 0000000000..823224abc8 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/script/IotProductScriptUpdateStatusReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.iot.controller.admin.product.vo.script; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - IoT 产品脚本状态更新 Request VO") +@Data +public class IotProductScriptUpdateStatusReqVO { + + @Schema(description = "脚本ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "脚本ID不能为空") + private Long id; + + @Schema(description = "状态(0=禁用 1=启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + 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/product/IotProductScriptDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java new file mode 100644 index 0000000000..6b973e6529 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductScriptDO.java @@ -0,0 +1,72 @@ +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.*; + +import java.time.LocalDateTime; + +/** + * IoT 产品脚本信息 DO + * + * @author 芋道源码 + */ +@TableName("iot_product_script") +@KeySequence("iot_product_script_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class IotProductScriptDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 产品ID + */ + private Long productId; + /** + * 产品唯一标识符 + */ + private String productKey; + /** + * 脚本类型(property_parser=属性解析,event_parser=事件解析,command_encoder=命令编码) + */ + private String scriptType; + /** + * 脚本内容 + */ + private String scriptContent; + /** + * 脚本语言 + */ + private String scriptLanguage; + /** + * 状态(0=禁用 1=启用) + */ + private Integer status; + /** + * 备注说明 + */ + private String remark; + /** + * 最后测试时间 + */ + private LocalDateTime lastTestTime; + /** + * 最后测试结果(0=失败 1=成功) + */ + private Integer lastTestResult; + /** + * 脚本版本号 + */ + private Integer version; + +} \ 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/IotProductScriptMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductScriptMapper.java new file mode 100644 index 0000000000..96c5ababdf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductScriptMapper.java @@ -0,0 +1,31 @@ +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.script.IotProductScriptPageReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * IoT 产品脚本信息 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface IotProductScriptMapper extends BaseMapperX { + + default PageResult selectPage(IotProductScriptPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(IotProductScriptDO::getProductId, reqVO.getProductId()) + .eqIfPresent(IotProductScriptDO::getProductKey, reqVO.getProductKey()) + .eqIfPresent(IotProductScriptDO::getScriptType, reqVO.getScriptType()) + .eqIfPresent(IotProductScriptDO::getScriptLanguage, reqVO.getScriptLanguage()) + .eqIfPresent(IotProductScriptDO::getStatus, reqVO.getStatus()) + .eqIfPresent(IotProductScriptDO::getRemark, reqVO.getRemark()) + .betweenIfPresent(IotProductScriptDO::getLastTestTime, reqVO.getLastTestTime()) + .betweenIfPresent(IotProductScriptDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(IotProductScriptDO::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/IotProductScriptService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptService.java new file mode 100644 index 0000000000..87486aaa6c --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptService.java @@ -0,0 +1,82 @@ +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.script.IotProductScriptPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * IoT 产品脚本信息 Service 接口 + * + * @author 芋道源码 + */ +public interface IotProductScriptService { + + /** + * 创建IoT 产品脚本信息 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProductScript(@Valid IotProductScriptSaveReqVO createReqVO); + + /** + * 更新IoT 产品脚本信息 + * + * @param updateReqVO 更新信息 + */ + void updateProductScript(@Valid IotProductScriptSaveReqVO updateReqVO); + + /** + * 删除IoT 产品脚本信息 + * + * @param id 编号 + */ + void deleteProductScript(Long id); + + /** + * 获得IoT 产品脚本信息 + * + * @param id 编号 + * @return IoT 产品脚本信息 + */ + IotProductScriptDO getProductScript(Long id); + + /** + * 获得IoT 产品脚本信息分页 + * + * @param pageReqVO 分页查询 + * @return IoT 产品脚本信息分页 + */ + PageResult getProductScriptPage(IotProductScriptPageReqVO pageReqVO); + + /** + * 获取产品的脚本列表 + * + * @param productId 产品ID + * @return 脚本列表 + */ + List getProductScriptListByProductId(Long productId); + + /** + * 测试产品脚本 + * + * @param testReqVO 测试请求 + * @return 测试结果 + */ + IotProductScriptTestRespVO testProductScript(@Valid IotProductScriptTestReqVO testReqVO); + + /** + * 更新产品脚本状态 + * + * @param id 脚本ID + * @param status 状态 + */ + void updateProductScriptStatus(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/product/IotProductScriptServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.java new file mode 100644 index 0000000000..d5a4aac72f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductScriptServiceImpl.java @@ -0,0 +1,219 @@ +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.script.IotProductScriptPageReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptSaveReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestReqVO; +import cn.iocoder.yudao.module.iot.controller.admin.product.vo.script.IotProductScriptTestRespVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductScriptDO; +import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductScriptMapper; +import cn.iocoder.yudao.module.iot.plugin.script.context.PluginScriptContext; +import cn.iocoder.yudao.module.iot.plugin.script.service.ScriptService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.time.LocalDateTime; +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_NOT_EXISTS; +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PRODUCT_SCRIPT_NOT_EXISTS; + +/** + * IoT 产品脚本信息 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class IotProductScriptServiceImpl implements IotProductScriptService { + + @Resource + private IotProductScriptMapper productScriptMapper; + + @Resource + private IotProductService productService; + + @Resource + private ScriptService scriptService; + + @Override + public Long createProductScript(IotProductScriptSaveReqVO createReqVO) { + // 验证产品是否存在 + validateProductExists(createReqVO.getProductId()); + + // 插入 + IotProductScriptDO productScript = BeanUtils.toBean(createReqVO, IotProductScriptDO.class); + // 初始化版本为1 + productScript.setVersion(1); + // 初始化测试相关字段 + productScript.setLastTestResult(null); + productScript.setLastTestTime(null); + productScriptMapper.insert(productScript); + // 返回 + return productScript.getId(); + } + + @Override + public void updateProductScript(IotProductScriptSaveReqVO updateReqVO) { + // 校验存在 + validateProductScriptExists(updateReqVO.getId()); + + // 获取旧的记录,保留版本号和测试信息 + IotProductScriptDO oldScript = getProductScript(updateReqVO.getId()); + + // 更新 + IotProductScriptDO updateObj = BeanUtils.toBean(updateReqVO, IotProductScriptDO.class); + // 更新版本号 + updateObj.setVersion(oldScript.getVersion() + 1); + // 保留测试相关信息 + updateObj.setLastTestTime(oldScript.getLastTestTime()); + updateObj.setLastTestResult(oldScript.getLastTestResult()); + productScriptMapper.updateById(updateObj); + } + + @Override + public void deleteProductScript(Long id) { + // 校验存在 + validateProductScriptExists(id); + // 删除 + productScriptMapper.deleteById(id); + } + + private void validateProductScriptExists(Long id) { + if (productScriptMapper.selectById(id) == null) { + throw exception(PRODUCT_SCRIPT_NOT_EXISTS); + } + } + + private void validateProductExists(Long productId) { + IotProductDO product = productService.getProduct(productId); + if (product == null) { + throw exception(PRODUCT_NOT_EXISTS); + } + } + + @Override + public IotProductScriptDO getProductScript(Long id) { + return productScriptMapper.selectById(id); + } + + @Override + public PageResult getProductScriptPage(IotProductScriptPageReqVO pageReqVO) { + return productScriptMapper.selectPage(pageReqVO); + } + + @Override + public List getProductScriptListByProductId(Long productId) { + return productScriptMapper.selectList(new LambdaQueryWrapper() + .eq(IotProductScriptDO::getProductId, productId) + .orderByDesc(IotProductScriptDO::getId)); + } + + @Override + public IotProductScriptTestRespVO testProductScript(IotProductScriptTestReqVO testReqVO) { + long startTime = System.currentTimeMillis(); + + try { + // 验证产品是否存在 + validateProductExists(testReqVO.getProductId()); + + // 根据ID获取已保存的脚本(如果有) + IotProductScriptDO existingScript = null; + if (testReqVO.getId() != null) { + existingScript = getProductScript(testReqVO.getId()); + } + + // 创建测试上下文 + PluginScriptContext context = new PluginScriptContext(); + IotProductDO product = productService.getProduct(testReqVO.getProductId()); + + // 设置设备上下文(使用产品信息,没有具体设备) + context.withDeviceContext(product.getProductKey(), null); + + // 设置输入参数 + Map params = new HashMap<>(); + params.put("input", testReqVO.getTestInput()); + params.put("productKey", product.getProductKey()); + params.put("scriptType", testReqVO.getScriptType()); + + // 根据脚本类型设置特定参数 + switch (testReqVO.getScriptType()) { + case "property_parser": + params.put("method", "property"); + break; + case "event_parser": + params.put("method", "event"); + params.put("identifier", "default"); + break; + case "command_encoder": + params.put("method", "command"); + break; + default: + // 默认不添加额外参数 + } + + // 添加所有参数到上下文 + for (Map.Entry entry : params.entrySet()) { + context.setParameter(entry.getKey(), entry.getValue()); + } + + // 执行脚本 + Object result = scriptService.executeScript( + testReqVO.getScriptLanguage(), + testReqVO.getScriptContent(), + context); + + // 更新测试结果(如果是已保存的脚本) + if (existingScript != null) { + IotProductScriptDO updateObj = new IotProductScriptDO(); + updateObj.setId(existingScript.getId()); + updateObj.setLastTestTime(LocalDateTime.now()); + updateObj.setLastTestResult(1); // 1表示成功 + productScriptMapper.updateById(updateObj); + } + + long executionTime = System.currentTimeMillis() - startTime; + return IotProductScriptTestRespVO.success(result, executionTime); + + } catch (Exception e) { + log.error("[testProductScript][测试脚本异常]", e); + + // 如果是已保存的脚本,更新测试失败状态 + if (testReqVO.getId() != null) { + try { + IotProductScriptDO updateObj = new IotProductScriptDO(); + updateObj.setId(testReqVO.getId()); + updateObj.setLastTestTime(LocalDateTime.now()); + updateObj.setLastTestResult(0); // 0表示失败 + productScriptMapper.updateById(updateObj); + } catch (Exception ex) { + log.error("[testProductScript][更新脚本测试结果异常]", ex); + } + } + + long executionTime = System.currentTimeMillis() - startTime; + return IotProductScriptTestRespVO.error(e.getMessage(), executionTime); + } + } + + @Override + public void updateProductScriptStatus(Long id, Integer status) { + // 校验存在 + validateProductScriptExists(id); + + // 更新状态 + IotProductScriptDO updateObj = new IotProductScriptDO(); + updateObj.setId(id); + updateObj.setStatus(status); + productScriptMapper.updateById(updateObj); + } +} \ No newline at end of file