From 48e605b3c965003fe4dba0d42af56d8845c97bef Mon Sep 17 00:00:00 2001 From: lzh Date: Thu, 23 Apr 2026 23:39:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(iot):=20=E6=81=A2=E5=A4=8D=20B2=20Controlle?= =?UTF-8?q?r/VO=20=E7=9A=84=20@PreAuthorize/@Schema/@Valid=20=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wave 2 三个 subagent 并行时,B19 subagent 看不到主会话后补的 rule/pom.xml web/security 依赖,把 B2 已写的注解全注释掉作为编译兜底。 rule/pom.xml 现已含 web/security/biz-tenant starter,恢复注解。 - IotRuleChainController: 7 @PreAuthorize + @Tag + 7 @Operation + @Parameter + @Valid + @Validated - IotRuleChainSaveReqVO: @Schema + @NotEmpty/@NotNull/@Valid(含 NodeVO/LinkVO 子 VO) - IotRuleChainRespVO: @Schema - IotRuleChainGraphVO: @Schema(含子类) - IotRuleChainPageReqVO: @Schema 落地 B2 AC9(REST API 所有端点带权限校验)。 测试:mvn test -pl viewsh-module-iot/viewsh-module-iot-rule → 17/17 全绿 Co-Authored-By: Claude Sonnet (annotation-restore subagent) Co-Authored-By: Claude Opus 4.7 (1M context, orchestrator) --- .../admin/IotRuleChainController.java | 43 +++++++----- .../admin/vo/IotRuleChainGraphVO.java | 40 ++++++----- .../admin/vo/IotRuleChainPageReqVO.java | 12 ++-- .../admin/vo/IotRuleChainRespVO.java | 28 ++++---- .../admin/vo/IotRuleChainSaveReqVO.java | 66 ++++++++++++------- 5 files changed, 114 insertions(+), 75 deletions(-) diff --git a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/IotRuleChainController.java b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/IotRuleChainController.java index 1d033e7a..a20788f3 100644 --- a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/IotRuleChainController.java +++ b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/IotRuleChainController.java @@ -7,64 +7,77 @@ import com.viewsh.module.iot.rule.controller.admin.vo.IotRuleChainPageReqVO; import com.viewsh.module.iot.rule.controller.admin.vo.IotRuleChainRespVO; import com.viewsh.module.iot.rule.controller.admin.vo.IotRuleChainSaveReqVO; import com.viewsh.module.iot.rule.service.IotRuleChainService; +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 com.viewsh.framework.common.pojo.CommonResult.success; -/** - * 管理后台 - IoT 规则链(DAG)Controller - * - *

权限:iot:rule:create / iot:rule:update / iot:rule:delete / iot:rule:query - * (由部署层 @PreAuthorize 保证,运行时需引入 spring-security) - */ +@Tag(name = "管理后台 - IoT 规则链") @RestController @RequestMapping("/iot/rule-chain") +@Validated public class IotRuleChainController { @Resource private IotRuleChainService ruleChainService; - // @PreAuthorize("@ss.hasPermission('iot:rule:create')") @PostMapping("/create") - public CommonResult createRuleChain(@RequestBody IotRuleChainSaveReqVO createReqVO) { + @Operation(summary = "创建规则链") + @PreAuthorize("@ss.hasPermission('iot:rule:create')") + public CommonResult createRuleChain(@Valid @RequestBody IotRuleChainSaveReqVO createReqVO) { return success(ruleChainService.createRuleChain(createReqVO)); } - // @PreAuthorize("@ss.hasPermission('iot:rule:update')") @PutMapping("/update") - public CommonResult updateRuleChain(@RequestBody IotRuleChainSaveReqVO updateReqVO) { + @Operation(summary = "更新规则链") + @PreAuthorize("@ss.hasPermission('iot:rule:update')") + public CommonResult updateRuleChain(@Valid @RequestBody IotRuleChainSaveReqVO updateReqVO) { ruleChainService.updateRuleChain(updateReqVO); return success(true); } - // @PreAuthorize("@ss.hasPermission('iot:rule:delete')") @DeleteMapping("/delete") + @Operation(summary = "删除规则链") + @Parameter(name = "id", description = "规则链编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:rule:delete')") public CommonResult deleteRuleChain(@RequestParam("id") Long id) { ruleChainService.deleteRuleChain(id); return success(true); } - // @PreAuthorize("@ss.hasPermission('iot:rule:query')") @GetMapping("/get/{id}") + @Operation(summary = "获得规则链") + @Parameter(name = "id", description = "规则链编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:rule:query')") public CommonResult getRuleChain(@PathVariable("id") Long id) { return success(ruleChainService.getRuleChain(id)); } - // @PreAuthorize("@ss.hasPermission('iot:rule:query')") @GetMapping("/graph/{id}") + @Operation(summary = "获得规则链图(含节点和连线)") + @Parameter(name = "id", description = "规则链编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:rule:query')") public CommonResult getRuleChainGraph(@PathVariable("id") Long id) { return success(ruleChainService.getRuleChainGraph(id)); } - // @PreAuthorize("@ss.hasPermission('iot:rule:query')") @GetMapping("/page") + @Operation(summary = "获得规则链分页") + @PreAuthorize("@ss.hasPermission('iot:rule:query')") public CommonResult> getRuleChainPage(IotRuleChainPageReqVO pageReqVO) { return success(ruleChainService.getRuleChainPage(pageReqVO)); } - // @PreAuthorize("@ss.hasPermission('iot:rule:update')") @PutMapping("/enable") + @Operation(summary = "启用规则链") + @Parameter(name = "id", description = "规则链编号", required = true) + @PreAuthorize("@ss.hasPermission('iot:rule:update')") public CommonResult enableRuleChain(@RequestParam("id") Long id) { ruleChainService.enableRuleChain(id); return success(true); diff --git a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainGraphVO.java b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainGraphVO.java index 3f1d1ce4..440bd7e4 100644 --- a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainGraphVO.java +++ b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainGraphVO.java @@ -1,5 +1,6 @@ package com.viewsh.module.iot.rule.controller.admin.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -10,52 +11,54 @@ import java.util.List; /** * 管理后台 - IoT 规则链图 VO(含 nodes + links 的完整图) */ +@Schema(description = "管理后台 - IoT 规则链图 VO(含节点和连线)") @Data @Builder @NoArgsConstructor @AllArgsConstructor public class IotRuleChainGraphVO { - /** 规则链基础信息 */ + @Schema(description = "规则链基础信息") private IotRuleChainRespVO chain; - /** 节点列表 */ + @Schema(description = "节点列表") private List nodes; - /** 连线列表 */ + @Schema(description = "连线列表") private List links; /** * 节点 VO */ + @Schema(description = "规则节点 VO") @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class NodeVO { - /** 节点编号 */ + @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - /** 所属规则链编号 */ + @Schema(description = "所属规则链编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long ruleChainId; - /** 节点名称 */ + @Schema(description = "节点名称", example = "温度触发器") private String name; - /** 节点类别(trigger/condition/action) */ + @Schema(description = "节点类别(trigger/condition/action)", requiredMode = Schema.RequiredMode.REQUIRED, example = "trigger") private String category; - /** Provider 标识 */ + @Schema(description = "Provider 标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device.attribute.trigger") private String type; - /** 节点配置(JSON) */ + @Schema(description = "节点配置(JSON)", example = "{\"attribute\":\"temperature\"}") private String configuration; - /** 画布 X 坐标 */ + @Schema(description = "画布 X 坐标", example = "100") private Integer positionX; - /** 画布 Y 坐标 */ + @Schema(description = "画布 Y 坐标", example = "200") private Integer positionY; } @@ -63,31 +66,32 @@ public class IotRuleChainGraphVO { /** * 连线 VO */ + @Schema(description = "规则连线 VO") @Data @Builder @NoArgsConstructor @AllArgsConstructor public static class LinkVO { - /** 连线编号 */ + @Schema(description = "连线编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - /** 所属规则链编号 */ + @Schema(description = "所属规则链编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long ruleChainId; - /** 源节点编号 */ + @Schema(description = "源节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long sourceNodeId; - /** 目标节点编号 */ + @Schema(description = "目标节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") private Long targetNodeId; - /** 关系类型(Success/Failure/True/False/Timeout/Skip) */ + @Schema(description = "关系类型(Success/Failure/True/False/Timeout/Skip)", requiredMode = Schema.RequiredMode.REQUIRED, example = "Success") private String relationType; - /** 连线条件(JSON) */ + @Schema(description = "连线条件(JSON)", example = "{}") private String condition; - /** 排序 */ + @Schema(description = "排序", example = "0") private Integer sortOrder; } diff --git a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainPageReqVO.java b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainPageReqVO.java index 13d9e192..839cb1cd 100644 --- a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainPageReqVO.java +++ b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainPageReqVO.java @@ -1,6 +1,7 @@ package com.viewsh.module.iot.rule.controller.admin.vo; import com.viewsh.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -13,24 +14,25 @@ import static com.viewsh.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_ /** * 管理后台 - IoT 规则链分页 Request VO */ +@Schema(description = "管理后台 - IoT 规则链分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class IotRuleChainPageReqVO extends PageParam { - /** 租户编号(内部使用) */ + @Schema(description = "租户编号(内部使用)", example = "1") private Long tenantId; - /** 规则链名称 */ + @Schema(description = "规则链名称", example = "温度告警") private String name; - /** 状态(0=禁用 1=启用 2=WARNING) */ + @Schema(description = "状态(0=禁用 1=启用 2=WARNING)", example = "1") private Integer status; - /** 规则链类型(SCENE/DATA/CUSTOM) */ + @Schema(description = "规则链类型(SCENE/DATA/CUSTOM)", example = "SCENE") private String type; - /** 创建时间 */ + @Schema(description = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; diff --git a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainRespVO.java b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainRespVO.java index a2e5483f..f57452c1 100644 --- a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainRespVO.java +++ b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainRespVO.java @@ -1,5 +1,6 @@ package com.viewsh.module.iot.rule.controller.admin.vo; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; @@ -7,46 +8,47 @@ import java.time.LocalDateTime; /** * 管理后台 - IoT 规则链 Response VO */ +@Schema(description = "管理后台 - IoT 规则链 Response VO") @Data public class IotRuleChainRespVO { - /** 规则链编号 */ + @Schema(description = "规则链编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - /** 规则链名称 */ + @Schema(description = "规则链名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "设备温度告警链") private String name; - /** 规则链描述 */ + @Schema(description = "规则链描述", example = "监控设备温度并触发告警") private String description; - /** 规则链类型(SCENE/DATA/CUSTOM) */ + @Schema(description = "规则链类型(SCENE/DATA/CUSTOM)", requiredMode = Schema.RequiredMode.REQUIRED, example = "SCENE") private String type; - /** 状态(0=禁用 1=启用 2=WARNING) */ + @Schema(description = "状态(0=禁用 1=启用 2=WARNING)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer status; - /** 优先级 */ + @Schema(description = "优先级", example = "10") private Integer priority; - /** 版本号(乐观锁) */ + @Schema(description = "版本号(乐观锁)", example = "1") private Long version; - /** 调试模式 */ + @Schema(description = "调试模式", example = "false") private Boolean debugMode; - /** 子系统编号 */ + @Schema(description = "子系统编号", example = "1") private Long subsystemId; - /** 产品编号 */ + @Schema(description = "产品编号", example = "1") private Long productId; - /** 设备编号 */ + @Schema(description = "设备编号", example = "0") private Long deviceId; - /** 创建时间 */ + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; - /** 更新时间 */ + @Schema(description = "更新时间") private LocalDateTime updateTime; } diff --git a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainSaveReqVO.java b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainSaveReqVO.java index 632e0c42..23e25573 100644 --- a/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainSaveReqVO.java +++ b/viewsh-module-iot/viewsh-module-iot-rule/src/main/java/com/viewsh/module/iot/rule/controller/admin/vo/IotRuleChainSaveReqVO.java @@ -1,5 +1,9 @@ package com.viewsh.module.iot.rule.controller.admin.vo; +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; import java.util.List; @@ -7,67 +11,77 @@ import java.util.List; /** * 管理后台 - IoT 规则链新增/修改 Request VO */ +@Schema(description = "管理后台 - IoT 规则链新增/修改 Request VO") @Data public class IotRuleChainSaveReqVO { - /** 规则链编号(修改时必填) */ + @Schema(description = "规则链编号(修改时必填)", example = "1024") private Long id; - /** 规则链名称(必填) */ + @Schema(description = "规则链名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "设备温度告警链") + @NotEmpty(message = "规则链名称不能为空") private String name; - /** 规则链描述 */ + @Schema(description = "规则链描述", example = "监控设备温度并触发告警") private String description; - /** 规则链类型(SCENE/DATA/CUSTOM,必填) */ + @Schema(description = "规则链类型(SCENE/DATA/CUSTOM)", requiredMode = Schema.RequiredMode.REQUIRED, example = "SCENE") + @NotEmpty(message = "规则链类型不能为空") private String type; - /** 优先级 */ + @Schema(description = "优先级", example = "10") private Integer priority; - /** 调试模式 */ + @Schema(description = "调试模式", example = "false") private Boolean debugMode; - /** 子系统编号(NULL=全局规则) */ + @Schema(description = "子系统编号(NULL=全局规则)", example = "1") private Long subsystemId; - /** 产品编号(NULL=不限产品) */ + @Schema(description = "产品编号(NULL=不限产品)", example = "1") private Long productId; - /** 设备编号(NULL/0=范围内所有设备) */ + @Schema(description = "设备编号(NULL/0=范围内所有设备)", example = "0") private Long deviceId; - /** 规则节点列表(必填) */ + @Schema(description = "规则节点列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "规则节点列表不能为空") + @Valid private List nodes; - /** 规则连线列表 */ + @Schema(description = "规则连线列表") + @Valid private List links; /** * 节点子 VO */ + @Schema(description = "规则节点 VO") @Data public static class NodeVO { - /** 节点编号(更新时填写,新增可不填) */ + @Schema(description = "节点编号(更新时填写,新增可不填)", example = "1") private Long id; - /** 节点名称 */ + @Schema(description = "节点名称", example = "温度触发器") private String name; - /** 节点类别(trigger/condition/action,必填) */ + @Schema(description = "节点类别(trigger/condition/action)", requiredMode = Schema.RequiredMode.REQUIRED, example = "trigger") + @NotEmpty(message = "节点类别不能为空") private String category; - /** Provider 标识(必填) */ + @Schema(description = "Provider 标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "device.attribute.trigger") + @NotEmpty(message = "节点类型(Provider 标识)不能为空") private String type; - /** 节点配置(JSON 字符串,必填) */ + @Schema(description = "节点配置(JSON 字符串)", requiredMode = Schema.RequiredMode.REQUIRED, example = "{\"attribute\":\"temperature\"}") + @NotEmpty(message = "节点配置不能为空") private String configuration; - /** 画布 X 坐标 */ + @Schema(description = "画布 X 坐标", example = "100") private Integer positionX; - /** 画布 Y 坐标 */ + @Schema(description = "画布 Y 坐标", example = "200") private Integer positionY; } @@ -75,25 +89,29 @@ public class IotRuleChainSaveReqVO { /** * 连线子 VO */ + @Schema(description = "规则连线 VO") @Data public static class LinkVO { - /** 连线编号(更新时填写,新增可不填) */ + @Schema(description = "连线编号(更新时填写,新增可不填)", example = "1") private Long id; - /** 源节点编号(必填) */ + @Schema(description = "源节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "源节点编号不能为空") private Long sourceNodeId; - /** 目标节点编号(必填) */ + @Schema(description = "目标节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "目标节点编号不能为空") private Long targetNodeId; - /** 关系类型(Success/Failure/True/False/Timeout/Skip,必填) */ + @Schema(description = "关系类型(Success/Failure/True/False/Timeout/Skip)", requiredMode = Schema.RequiredMode.REQUIRED, example = "Success") + @NotEmpty(message = "关系类型不能为空") private String relationType; - /** 连线条件(JSON,可选) */ + @Schema(description = "连线条件(JSON,可选)", example = "{}") private String condition; - /** 排序 */ + @Schema(description = "排序", example = "0") private Integer sortOrder; }