From f213510b036356258f19b02324c06527104e4df9 Mon Sep 17 00:00:00 2001 From: lzh Date: Sun, 22 Mar 2026 15:00:07 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ops):=20=E5=B7=A1=E6=A3=80=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E9=87=8D=E6=9E=84=20=E2=80=94=20=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E5=89=8D=E7=A7=BB=E3=80=81=E5=88=97=E8=A1=A8?= =?UTF-8?q?/=E8=AF=A6=E6=83=85=E5=88=86=E7=A6=BB=E3=80=81=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E8=81=9A=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接口变更: - 删除 POST /verify-location,位置校验改为前端本地蓝牙信标匹配 - 新增 GET /record/get 巡检记录详情(含明细项、照片) - GET /list-by-area 升级为返回完整巡检表单(区域+检查项+信标配置) - GET /record/page 返回类型改为 VO,新增区域全路径名称和巡检员姓名 - 提交和表单接口增加 inspector 角色校验 代码质量(Code Review 修复): - 提取 buildAreaFullName 至 OpsBusAreaMapper 消除两个 Service 的重复 - 新增 buildAreaFullNameMap 批量方法,修复分页场景 N+1 查询 - getRecordDetail 中 adminUserApi 改用 getUserMap + try-catch 降级 - InspectionTemplateServiceImpl 去掉 ObjectMapper 依赖,直接 Map 取值 - RSSI 阈值取最宽松值逻辑添加语义注释 - 巡检错误码从 1-020-003 迁移至 1-020-004,修复与安保模块的码段冲突 - InspectionRecordDetailRespVO.photos 使用 @OssPresignUrl 自动预签名 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../admin/inspection/vo/DetectedBeaconVO.java | 20 --- .../vo/InspectionBeaconConfigVO.java | 42 +++++ .../vo/InspectionBoundBeaconVO.java | 25 +++ .../inspection/vo/InspectionFormRespVO.java | 33 ++++ .../vo/InspectionRecordDetailRespVO.java | 31 ++++ .../vo/InspectionRecordItemRespVO.java | 36 +++++ .../inspection/vo/InspectionRecordRespVO.java | 18 ++- .../inspection/vo/InspectionSubmitReqVO.java | 4 + .../vo/InspectionTemplateRespVO.java | 5 +- .../vo/InspectionTemplateSaveReqVO.java | 5 +- .../inspection/vo/LocationVerifyResultVO.java | 28 ---- .../inspection/OpsInspectionRecordDO.java | 5 + .../inspection/OpsInspectionTemplateDO.java | 4 + .../inspection/InspectionLocationService.java | 25 --- .../InspectionLocationServiceImpl.java | 100 ------------ .../inspection/InspectionRecordService.java | 17 +- .../InspectionRecordServiceImpl.java | 150 ++++++++++++++++-- .../inspection/InspectionTemplateService.java | 9 +- .../InspectionTemplateServiceImpl.java | 134 ++++++++++++++-- .../module/ops/enums/ErrorCodeConstants.java | 7 +- .../ops/dal/mysql/area/OpsBusAreaMapper.java | 90 ++++++++++- .../inspection/InspectionController.java | 31 ++-- .../InspectionTemplateController.java | 10 +- .../rpc/config/RpcConfiguration.java | 2 + 24 files changed, 585 insertions(+), 246 deletions(-) delete mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/DetectedBeaconVO.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBeaconConfigVO.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBoundBeaconVO.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionFormRespVO.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordDetailRespVO.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordItemRespVO.java delete mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/LocationVerifyResultVO.java delete mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java delete mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationServiceImpl.java diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/DetectedBeaconVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/DetectedBeaconVO.java deleted file mode 100644 index d828f92..0000000 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/DetectedBeaconVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.viewsh.module.ops.environment.controller.admin.inspection.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "蓝牙信标检测数据") -@Data -public class DetectedBeaconVO { - - @Schema(description = "信标MAC地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "F0:C8:60:1D:10:BB") - @NotBlank(message = "信标MAC地址不能为空") - private String mac; - - @Schema(description = "信号强度(RSSI)", requiredMode = Schema.RequiredMode.REQUIRED, example = "-65") - @NotNull(message = "信号强度不能为空") - private Integer rssi; - -} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBeaconConfigVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBeaconConfigVO.java new file mode 100644 index 0000000..c47ceed --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBeaconConfigVO.java @@ -0,0 +1,42 @@ +package com.viewsh.module.ops.environment.controller.admin.inspection.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 巡检 - 蓝牙信标扫描配置 VO + *

+ * 下发给小程序,用于: + *

    + *
  1. uuid → 传入 wx.startBeaconDiscovery 启动扫描
  2. + *
  3. boundBeacons → onBeaconUpdate 回调中本地匹配 (major + minor + rssi)
  4. + *
  5. timeoutMs → 扫描总超时,超时未匹配则标记位置异常
  6. + *
+ * 该字段为 null 时表示区域未绑定信标,前端跳过位置校验。 + */ +@Schema(description = "巡检 - 蓝牙信标扫描配置") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class InspectionBeaconConfigVO { + + @Schema(description = "iBeacon UUID(传入 wx.startBeaconDiscovery)", + example = "FDA50693-A4E2-4FB1-AFCF-C6EB07647825") + private String uuid; + + @Schema(description = "该区域绑定的信标特征列表(前端本地比对用)") + private List boundBeacons; + + @Schema(description = "RSSI 阈值(信号强度低于此值视为不在范围内)", example = "-70") + private Integer rssiThreshold; + + @Schema(description = "扫描超时时间(毫秒),超时未匹配则标记位置异常", example = "15000") + private Integer timeoutMs; + +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBoundBeaconVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBoundBeaconVO.java new file mode 100644 index 0000000..a6e309b --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionBoundBeaconVO.java @@ -0,0 +1,25 @@ +package com.viewsh.module.ops.environment.controller.admin.inspection.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 巡检 - 区域绑定的信标特征 VO + *

+ * 下发给前端做本地位置匹配(major + minor 唯一标识一个信标) + */ +@Schema(description = "巡检 - 区域绑定信标特征") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class InspectionBoundBeaconVO { + + @Schema(description = "iBeacon Major 值", example = "1") + private Integer major; + + @Schema(description = "iBeacon Minor 值", example = "100") + private Integer minor; + +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionFormRespVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionFormRespVO.java new file mode 100644 index 0000000..a9401a2 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionFormRespVO.java @@ -0,0 +1,33 @@ +package com.viewsh.module.ops.environment.controller.admin.inspection.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 管理后台 - 巡检表单(扫码后返回)Response VO + */ +@Schema(description = "管理后台 - 巡检表单 Response VO") +@Data +public class InspectionFormRespVO { + + @Schema(description = "区域ID", example = "1024") + private Long areaId; + + @Schema(description = "区域名称", example = "男卫") + private String areaName; + + @Schema(description = "区域全路径名称", example = "A园区/1号楼/2层/男卫") + private String areaFullName; + + @Schema(description = "功能类型", example = "MALE_TOILET") + private String functionType; + + @Schema(description = "检查项列表") + private List items; + + @Schema(description = "蓝牙信标扫描配置(null 表示该区域无信标,跳过位置校验)") + private InspectionBeaconConfigVO beaconConfig; + +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordDetailRespVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordDetailRespVO.java new file mode 100644 index 0000000..fc41e91 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordDetailRespVO.java @@ -0,0 +1,31 @@ +package com.viewsh.module.ops.environment.controller.admin.inspection.vo; + +import com.viewsh.framework.web.core.presign.annotation.OssPresignUrl; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 管理后台 - 巡检记录详情 Response VO(含明细项) + */ +@Schema(description = "管理后台 - 巡检记录详情 Response VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class InspectionRecordDetailRespVO extends InspectionRecordRespVO { + + @Schema(description = "备注", example = "卫生间地面有污渍") + private String remark; + + @Schema(description = "快捷标签", example = "[\"地面污渍\",\"垃圾未清理\"]") + private List tags; + + @OssPresignUrl + @Schema(description = "巡检照片URL列表", example = "[\"https://oss.example.com/a.jpg\"]") + private List photos; + + @Schema(description = "巡检明细项列表") + private List items; + +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordItemRespVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordItemRespVO.java new file mode 100644 index 0000000..547c46f --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordItemRespVO.java @@ -0,0 +1,36 @@ +package com.viewsh.module.ops.environment.controller.admin.inspection.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 管理后台 - 巡检明细项 Response VO + */ +@Schema(description = "管理后台 - 巡检明细项 Response VO") +@Data +public class InspectionRecordItemRespVO { + + @Schema(description = "明细ID", example = "1") + private Long id; + + @Schema(description = "模板检查项ID", example = "501") + private Long templateId; + + @Schema(description = "检查项标题", example = "地面洁净度") + private String itemTitle; + + @Schema(description = "检查项描述", example = "基本无污渍、水渍") + private String itemDescription; + + @Schema(description = "是否合格", example = "true") + private Boolean isPassed; + + @Schema(description = "备注", example = "地面有明显污渍") + private String remark; + + @Schema(description = "快捷标签", example = "[\"地面污渍\"]") + private List tags; + +} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordRespVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordRespVO.java index 6f04f1e..6fd5dac 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordRespVO.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionRecordRespVO.java @@ -4,8 +4,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; -import java.util.List; +/** + * 管理后台 - 巡检记录(列表页)Response VO + */ @Schema(description = "管理后台 - 巡检记录 Response VO") @Data public class InspectionRecordRespVO { @@ -16,28 +18,28 @@ public class InspectionRecordRespVO { @Schema(description = "区域ID", example = "1") private Long areaId; + @Schema(description = "区域全路径名称", example = "A园区/1号楼/2层/男卫") + private String areaFullName; + @Schema(description = "巡检员用户ID", example = "100") private Long inspectorId; + @Schema(description = "巡检员姓名", example = "张三") + private String inspectorName; + @Schema(description = "位置是否异常(0正常 1异常)", example = "0") private Integer isLocationException; @Schema(description = "巡检结果,参见 InspectionResultEnum(0不合格 1合格)", example = "1") private Integer resultStatus; - @Schema(description = "备注", example = "检查完成") - private String remark; - @Schema(description = "归属判定结果,参见 InspectionAttributionEnum(1个人责任 2突发状况 3正常)", example = "3") private Integer attributionResult; @Schema(description = "整改工单ID", example = "2048") private Long generatedOrderId; - @Schema(description = "快捷标签", example = "[\"地面污渍\",\"垃圾未清理\"]") - private List tags; - - @Schema(description = "创建时间") + @Schema(description = "巡检时间") private LocalDateTime createTime; } diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionSubmitReqVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionSubmitReqVO.java index 039ac1c..454c817 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionSubmitReqVO.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionSubmitReqVO.java @@ -28,6 +28,10 @@ public class InspectionSubmitReqVO { @Size(max = 10, message = "标签数量不能超过10个") private List<@Size(max = 32, message = "单个标签长度不能超过32") String> tags; + @Schema(description = "巡检照片URL列表", example = "[\"https://oss.example.com/a.jpg\"]") + @Size(max = 9, message = "照片数量不能超过9张") + private List photos; + @Schema(description = "巡检明细项列表", requiredMode = Schema.RequiredMode.REQUIRED) @NotEmpty(message = "巡检明细项不能为空") @Valid diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateRespVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateRespVO.java index f9c6f35..19d8a55 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateRespVO.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateRespVO.java @@ -15,9 +15,12 @@ public class InspectionTemplateRespVO { @Schema(description = "功能类型", example = "TOILET") private String functionType; - @Schema(description = "检查项标题", example = "地面是否干净") + @Schema(description = "检查项标题", example = "地面洁净度") private String itemTitle; + @Schema(description = "检查项描述(合格标准说明)", example = "基本无污渍、水渍") + private String itemDescription; + @Schema(description = "排序序号", example = "1") private Integer sortOrder; diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateSaveReqVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateSaveReqVO.java index 1b516bd..0e80005 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateSaveReqVO.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/InspectionTemplateSaveReqVO.java @@ -16,10 +16,13 @@ public class InspectionTemplateSaveReqVO { @NotBlank(message = "功能类型不能为空") private String functionType; - @Schema(description = "检查项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "地面是否干净") + @Schema(description = "检查项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "地面洁净度") @NotBlank(message = "检查项标题不能为空") private String itemTitle; + @Schema(description = "检查项描述(合格标准说明)", example = "基本无污渍、水渍") + private String itemDescription; + @Schema(description = "排序序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @NotNull(message = "排序序号不能为空") private Integer sortOrder; diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/LocationVerifyResultVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/LocationVerifyResultVO.java deleted file mode 100644 index 4a466f9..0000000 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/LocationVerifyResultVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.viewsh.module.ops.environment.controller.admin.inspection.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema(description = "蓝牙位置校验结果") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class LocationVerifyResultVO { - - @Schema(description = "校验是否通过") - private Boolean passed; - - @Schema(description = "校验消息") - private String message; - - public static LocationVerifyResultVO success() { - return new LocationVerifyResultVO(true, "位置校验通过"); - } - - public static LocationVerifyResultVO fail(String message) { - return new LocationVerifyResultVO(false, message); - } - -} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionRecordDO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionRecordDO.java index 6bb3678..8910cc3 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionRecordDO.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionRecordDO.java @@ -69,5 +69,10 @@ public class OpsInspectionRecordDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private List tags; + /** + * 巡检照片 URL 列表 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List photos; } diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionTemplateDO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionTemplateDO.java index 5d2913a..5d5dcbd 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionTemplateDO.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/dal/dataobject/inspection/OpsInspectionTemplateDO.java @@ -32,6 +32,10 @@ public class OpsInspectionTemplateDO extends BaseDO { * 检查项标题 */ private String itemTitle; + /** + * 检查项描述(合格标准说明) + */ + private String itemDescription; /** * 排序序号 */ diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java deleted file mode 100644 index f2edb10..0000000 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.viewsh.module.ops.environment.service.inspection; - -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.DetectedBeaconVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.LocationVerifyResultVO; - -import java.util.List; - -/** - * 巡检蓝牙位置校验 Service - */ -public interface InspectionLocationService { - - /** - * 校验巡检人员是否在指定区域 - *

- * 逻辑:查询该区域绑定的 BEACON 类型设备, - * 匹配检测到的蓝牙信标列表,至少1个绑定信标匹配且 RSSI 大于阈值即通过 - * - * @param areaId 区域ID - * @param detectedBeacons 检测到的蓝牙信标列表 - * @return 校验结果 - */ - LocationVerifyResultVO verifyLocation(Long areaId, List detectedBeacons); - -} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationServiceImpl.java deleted file mode 100644 index c46bbd9..0000000 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationServiceImpl.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.viewsh.module.ops.environment.service.inspection; - -import com.viewsh.module.ops.dal.dataobject.area.OpsAreaDeviceRelationDO; -import com.viewsh.module.ops.dal.mysql.area.OpsAreaDeviceRelationMapper; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.DetectedBeaconVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.LocationVerifyResultVO; -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; - -/** - * 巡检蓝牙位置校验 Service 实现 - */ -@Service -@Validated -@Slf4j -public class InspectionLocationServiceImpl implements InspectionLocationService { - - private static final String RELATION_TYPE_BEACON = "BEACON"; - private static final int DEFAULT_RSSI_THRESHOLD = -70; - - @Resource - private OpsAreaDeviceRelationMapper areaDeviceRelationMapper; - - @Override - public LocationVerifyResultVO verifyLocation(Long areaId, List detectedBeacons) { - if (detectedBeacons == null || detectedBeacons.isEmpty()) { - return LocationVerifyResultVO.fail("未检测到蓝牙信标"); - } - - // 查询该区域绑定的 BEACON 设备 - List beaconRelations = - areaDeviceRelationMapper.selectListByAreaIdAndRelationType(areaId, RELATION_TYPE_BEACON); - if (beaconRelations.isEmpty()) { - // 区域未绑定信标,视为不需要位置校验,直接通过 - log.info("[verifyLocation] 区域 {} 未绑定蓝牙信标,跳过位置校验", areaId); - return LocationVerifyResultVO.success(); - } - - // 匹配逻辑:至少1个绑定信标匹配且 RSSI > 阈值 - for (OpsAreaDeviceRelationDO relation : beaconRelations) { - String boundBeaconMac = extractBeaconMac(relation); - if (boundBeaconMac == null) { - continue; - } - int rssiThreshold = extractRssiThreshold(relation); - - for (DetectedBeaconVO detected : detectedBeacons) { - if (boundBeaconMac.equalsIgnoreCase(detected.getMac()) - && detected.getRssi() >= rssiThreshold) { - log.info("[verifyLocation] 区域 {} 位置校验通过,匹配信标 {},RSSI={}", - areaId, detected.getMac(), detected.getRssi()); - return LocationVerifyResultVO.success(); - } - } - } - - return LocationVerifyResultVO.fail("未匹配到该区域的蓝牙信标,请确认是否在正确位置"); - } - - /** - * 从设备关联的 configData 中提取信标 MAC 地址 - */ - private String extractBeaconMac(OpsAreaDeviceRelationDO relation) { - Map configData = relation.getConfigData(); - if (configData == null) { - // 退回到 deviceKey 作为 MAC 标识 - return relation.getDeviceKey(); - } - Object beaconMac = configData.get("beaconMac"); - if (beaconMac != null) { - return beaconMac.toString(); - } - return relation.getDeviceKey(); - } - - /** - * 从设备关联的 configData 中提取 RSSI 阈值 - */ - @SuppressWarnings("unchecked") - private int extractRssiThreshold(OpsAreaDeviceRelationDO relation) { - Map configData = relation.getConfigData(); - if (configData == null) { - return DEFAULT_RSSI_THRESHOLD; - } - Object enter = configData.get("enter"); - if (enter instanceof Map) { - Object threshold = ((Map) enter).get("rssiThreshold"); - if (threshold instanceof Number) { - return ((Number) threshold).intValue(); - } - } - return DEFAULT_RSSI_THRESHOLD; - } - -} diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordService.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordService.java index b92876a..9dbbfd4 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordService.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordService.java @@ -1,10 +1,7 @@ package com.viewsh.module.ops.environment.service.inspection; import com.viewsh.framework.common.pojo.PageResult; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionRecordPageReqVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionStatsReqVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionStatsRespVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionSubmitReqVO; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.*; import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO; /** @@ -22,12 +19,20 @@ public interface InspectionRecordService { Long submitInspection(InspectionSubmitReqVO submitReqVO, Long inspectorId); /** - * 获得巡检记录分页 + * 获得巡检记录分页(含区域名称、巡检员姓名) * * @param pageReqVO 分页查询 * @return 巡检记录分页 */ - PageResult getRecordPage(InspectionRecordPageReqVO pageReqVO); + PageResult getRecordPage(InspectionRecordPageReqVO pageReqVO); + + /** + * 获得巡检记录详情(含明细项) + * + * @param id 巡检记录ID + * @return 巡检记录详情 + */ + InspectionRecordDetailRespVO getRecordDetail(Long id); /** * 获得巡检统计(合格率、不合格热点区域) diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordServiceImpl.java index a3a2b5a..89259c4 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordServiceImpl.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionRecordServiceImpl.java @@ -2,19 +2,26 @@ package com.viewsh.module.ops.environment.service.inspection; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.viewsh.framework.common.pojo.PageResult; +import com.viewsh.framework.common.util.http.HttpUtils; import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX; import com.viewsh.module.ops.enums.InspectionResultEnum; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionRecordPageReqVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionStatsReqVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionStatsRespVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionSubmitItemVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionSubmitReqVO; +import com.viewsh.framework.common.util.object.BeanUtils; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.*; +import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionTemplateDO; +import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionTemplateMapper; +import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO; +import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper; +import com.viewsh.module.system.api.user.AdminUserApi; +import com.viewsh.module.system.api.user.dto.AdminUserRespDTO; import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO; import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordItemDO; import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordItemMapper; import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; + +import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.viewsh.module.ops.enums.ErrorCodeConstants.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; @@ -23,8 +30,8 @@ import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; /** * 巡检记录 Service 实现 @@ -40,12 +47,24 @@ public class InspectionRecordServiceImpl implements InspectionRecordService { @Resource private OpsInspectionRecordItemMapper inspectionRecordItemMapper; + @Resource + private OpsBusAreaMapper opsBusAreaMapper; + + @Resource + private OpsInspectionTemplateMapper inspectionTemplateMapper; + + @Resource + private AdminUserApi adminUserApi; + @Resource private InspectionAsyncHandler inspectionAsyncHandler; @Override @Transactional(rollbackFor = Exception.class) public Long submitInspection(InspectionSubmitReqVO submitReqVO, Long inspectorId) { + // 0. 前置校验:区域是否存在且启用 + validateArea(submitReqVO.getAreaId()); + // 1. 判定巡检结果:任一项不合格 → 整体不合格 boolean allPassed = submitReqVO.getItems().stream() .allMatch(InspectionSubmitItemVO::getIsPassed); @@ -59,6 +78,7 @@ public class InspectionRecordServiceImpl implements InspectionRecordService { .resultStatus(resultStatus) .remark(submitReqVO.getRemark()) .tags(submitReqVO.getTags()) + .photos(normalizePhotoUrls(submitReqVO.getPhotos())) .build(); inspectionRecordMapper.insert(record); @@ -90,13 +110,41 @@ public class InspectionRecordServiceImpl implements InspectionRecordService { } @Override - public PageResult getRecordPage(InspectionRecordPageReqVO pageReqVO) { - return inspectionRecordMapper.selectPage(pageReqVO, new LambdaQueryWrapperX() - .eqIfPresent(OpsInspectionRecordDO::getAreaId, pageReqVO.getAreaId()) - .eqIfPresent(OpsInspectionRecordDO::getInspectorId, pageReqVO.getInspectorId()) - .eqIfPresent(OpsInspectionRecordDO::getResultStatus, pageReqVO.getResultStatus()) - .betweenIfPresent(OpsInspectionRecordDO::getCreateTime, pageReqVO.getCreateTime()) - .orderByDesc(OpsInspectionRecordDO::getId)); + public PageResult getRecordPage(InspectionRecordPageReqVO pageReqVO) { + PageResult pageResult = inspectionRecordMapper.selectPage(pageReqVO, + new LambdaQueryWrapperX() + .eqIfPresent(OpsInspectionRecordDO::getAreaId, pageReqVO.getAreaId()) + .eqIfPresent(OpsInspectionRecordDO::getInspectorId, pageReqVO.getInspectorId()) + .eqIfPresent(OpsInspectionRecordDO::getResultStatus, pageReqVO.getResultStatus()) + .betweenIfPresent(OpsInspectionRecordDO::getCreateTime, pageReqVO.getCreateTime()) + .orderByDesc(OpsInspectionRecordDO::getId)); + + List records = pageResult.getList(); + if (records.isEmpty()) { + return new PageResult<>(Collections.emptyList(), pageResult.getTotal()); + } + + // 批量查区域,一次性拼全路径名称(避免 N+1) + Set areaIds = records.stream().map(OpsInspectionRecordDO::getAreaId).collect(Collectors.toSet()); + List areas = opsBusAreaMapper.selectBatchIds(areaIds); + Map areaFullNameMap = opsBusAreaMapper.buildAreaFullNameMap(areas); + + // 批量查巡检员姓名 + Set inspectorIds = records.stream().map(OpsInspectionRecordDO::getInspectorId).collect(Collectors.toSet()); + Map userMap = adminUserApi.getUserMap(inspectorIds); + + // 组装 VO + List voList = records.stream().map(record -> { + InspectionRecordRespVO vo = BeanUtils.toBean(record, InspectionRecordRespVO.class); + vo.setAreaFullName(areaFullNameMap.get(record.getAreaId())); + AdminUserRespDTO user = userMap.get(record.getInspectorId()); + if (user != null) { + vo.setInspectorName(user.getNickname()); + } + return vo; + }).toList(); + + return new PageResult<>(voList, pageResult.getTotal()); } @Override @@ -151,4 +199,78 @@ public class InspectionRecordServiceImpl implements InspectionRecordService { .build(); } + @Override + public InspectionRecordDetailRespVO getRecordDetail(Long id) { + // 1. 查主记录 + OpsInspectionRecordDO record = inspectionRecordMapper.selectById(id); + if (record == null) { + throw exception(INSPECTION_RECORD_NOT_FOUND); + } + + // 2. 转换基础字段 + InspectionRecordDetailRespVO detailVO = BeanUtils.toBean(record, InspectionRecordDetailRespVO.class); + + // 3. 区域全路径名称 + OpsBusAreaDO area = opsBusAreaMapper.selectById(record.getAreaId()); + if (area != null) { + detailVO.setAreaFullName(opsBusAreaMapper.buildAreaFullName(area)); + } + + // 4. 巡检员姓名(降级处理,避免远程调用失败导致整个详情接口不可用) + try { + Map userMap = adminUserApi.getUserMap(Set.of(record.getInspectorId())); + AdminUserRespDTO user = userMap.get(record.getInspectorId()); + if (user != null) { + detailVO.setInspectorName(user.getNickname()); + } + } catch (Exception e) { + log.warn("[getRecordDetail] 查询巡检员姓名失败: inspectorId={}", record.getInspectorId(), e); + } + + // 5. 查询明细项 + 关联模板标题 + List items = inspectionRecordItemMapper.selectListByRecordId(id); + // 批量查模板 + Set templateIds = items.stream().map(OpsInspectionRecordItemDO::getTemplateId).collect(Collectors.toSet()); + Map templateMap = templateIds.isEmpty() + ? Collections.emptyMap() + : inspectionTemplateMapper.selectBatchIds(templateIds).stream() + .collect(Collectors.toMap(OpsInspectionTemplateDO::getId, t -> t)); + + List itemVOs = items.stream().map(item -> { + InspectionRecordItemRespVO itemVO = BeanUtils.toBean(item, InspectionRecordItemRespVO.class); + OpsInspectionTemplateDO template = templateMap.get(item.getTemplateId()); + if (template != null) { + itemVO.setItemTitle(template.getItemTitle()); + itemVO.setItemDescription(template.getItemDescription()); + } + return itemVO; + }).toList(); + detailVO.setItems(itemVOs); + + return detailVO; + } + + /** + * 校验区域是否存在且已启用 + */ + private void validateArea(Long areaId) { + OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId); + if (area == null) { + throw exception(AREA_NOT_FOUND); + } + if (!Boolean.TRUE.equals(area.getIsActive())) { + throw exception(INSPECTION_AREA_NOT_ACTIVE); + } + } + + private List normalizePhotoUrls(List photos) { + if (photos == null || photos.isEmpty()) { + return photos; + } + return photos.stream() + .filter(Objects::nonNull) + .map(HttpUtils::removeUrlQuery) + .toList(); + } + } diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateService.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateService.java index 9f55997..d16a141 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateService.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateService.java @@ -3,6 +3,7 @@ package com.viewsh.module.ops.environment.service.inspection; import com.viewsh.framework.common.pojo.PageResult; import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplatePageReqVO; import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplateSaveReqVO; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionFormRespVO; import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionTemplateDO; import java.util.List; @@ -59,13 +60,13 @@ public interface InspectionTemplateService { List getTemplateListByFunctionType(String functionType); /** - * 按区域ID获取动态巡检表单(检查项列表) + * 按区域ID获取巡检表单(区域信息 + 检查项列表) *

- * 流程: area_id → 查 ops_bus_area 获取 function_type → 查 ops_inspection_template 加载检查项 + * 流程: area_id → 校验区域 → 查 function_type → 加载检查项 → 拼装区域全路径名称 * * @param areaId 区域ID - * @return 检查项列表(按 sortOrder 排序) + * @return 巡检表单(含区域信息和检查项列表) */ - List getTemplateListByAreaId(Long areaId); + InspectionFormRespVO getInspectionFormByAreaId(Long areaId); } diff --git a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateServiceImpl.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateServiceImpl.java index 2b07f1b..ac4cdf9 100644 --- a/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateServiceImpl.java +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionTemplateServiceImpl.java @@ -3,10 +3,12 @@ package com.viewsh.module.ops.environment.service.inspection; import com.viewsh.framework.common.pojo.PageResult; import com.viewsh.framework.common.util.object.BeanUtils; import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplatePageReqVO; -import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplateSaveReqVO; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.*; import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO; +import com.viewsh.module.ops.dal.dataobject.area.OpsAreaDeviceRelationDO; import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper; +import com.viewsh.module.ops.dal.mysql.area.OpsAreaDeviceRelationMapper; +import lombok.extern.slf4j.Slf4j; import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionTemplateDO; import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionTemplateMapper; import jakarta.annotation.Resource; @@ -14,26 +16,35 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; -import java.util.Collections; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; import static com.viewsh.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.viewsh.module.ops.enums.ErrorCodeConstants.AREA_NOT_FOUND; +import static com.viewsh.module.ops.enums.ErrorCodeConstants.INSPECTION_AREA_NOT_ACTIVE; import static com.viewsh.module.ops.enums.ErrorCodeConstants.INSPECTION_TEMPLATE_NOT_FOUND; /** * 巡检检查项模板 Service 实现 */ +@Slf4j @Service @Validated public class InspectionTemplateServiceImpl implements InspectionTemplateService { + private static final String RELATION_TYPE_BEACON = "BEACON"; + private static final int DEFAULT_RSSI_THRESHOLD = -70; + private static final int DEFAULT_TIMEOUT_MS = 15000; + @Resource private OpsInspectionTemplateMapper inspectionTemplateMapper; @Resource private OpsBusAreaMapper opsBusAreaMapper; + @Resource + private OpsAreaDeviceRelationMapper areaDeviceRelationMapper; + @Override @Transactional(rollbackFor = Exception.class) public Long createTemplate(InspectionTemplateSaveReqVO createReqVO) { @@ -83,16 +94,121 @@ public class InspectionTemplateServiceImpl implements InspectionTemplateService } @Override - public List getTemplateListByAreaId(Long areaId) { + public InspectionFormRespVO getInspectionFormByAreaId(Long areaId) { + // 1. 校验区域 OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId); if (area == null) { throw exception(AREA_NOT_FOUND); } - String functionType = area.getFunctionType(); - if (functionType == null || functionType.isEmpty()) { - return Collections.emptyList(); + if (!Boolean.TRUE.equals(area.getIsActive())) { + throw exception(INSPECTION_AREA_NOT_ACTIVE); } - return inspectionTemplateMapper.selectListByFunctionType(functionType); + + // 2. 查询检查项 + String functionType = area.getFunctionType(); + List templates = (functionType == null || functionType.isEmpty()) + ? Collections.emptyList() + : inspectionTemplateMapper.selectListByFunctionType(functionType); + + // 3. 查询区域绑定的信标设备,拼装扫描配置 + InspectionBeaconConfigVO beaconConfig = buildBeaconConfig(areaId); + + // 4. 拼装返回 + InspectionFormRespVO formVO = new InspectionFormRespVO(); + formVO.setAreaId(area.getId()); + formVO.setAreaName(area.getAreaName()); + formVO.setAreaFullName(opsBusAreaMapper.buildAreaFullName(area)); + formVO.setFunctionType(functionType); + formVO.setItems(BeanUtils.toBean(templates, InspectionTemplateRespVO.class)); + formVO.setBeaconConfig(beaconConfig); + return formVO; + } + + /** + * 构建信标扫描配置 + *

+ * 查询区域绑定的 BEACON 设备,从 configData.beaconPresence 中提取配置, + * 返回 null 表示该区域无信标,前端应跳过位置校验。 + */ + private InspectionBeaconConfigVO buildBeaconConfig(Long areaId) { + List beaconRelations = + areaDeviceRelationMapper.selectListByAreaIdAndRelationType(areaId, RELATION_TYPE_BEACON); + if (beaconRelations.isEmpty()) { + return null; + } + + String uuid = null; + List boundBeacons = new ArrayList<>(); + int rssiThreshold = DEFAULT_RSSI_THRESHOLD; + + for (OpsAreaDeviceRelationDO relation : beaconRelations) { + Map presence = extractBeaconPresenceMap(relation.getConfigData()); + if (presence == null) { + continue; + } + + // UUID(所有信标共用,取第一个有效值) + String beaconUuid = (String) presence.get("beaconUuid"); + if (uuid == null && beaconUuid != null) { + uuid = beaconUuid; + } + + // major + minor + Integer major = toInteger(presence.get("major")); + Integer minor = toInteger(presence.get("minor")); + if (major != null && minor != null) { + boundBeacons.add(new InspectionBoundBeaconVO(major, minor)); + } + + // RSSI 阈值:多个信标取最小值(即最宽松),确保巡检员在任一信标附近即算到位。 + // RSSI 越小(越负)表示信号越弱,取最小值意味着允许最弱信号通过。 + @SuppressWarnings("unchecked") + Map enter = (Map) presence.get("enter"); + if (enter != null) { + Integer threshold = toInteger(enter.get("rssiThreshold")); + if (threshold != null && threshold < rssiThreshold) { + rssiThreshold = threshold; + } + } + } + + if (uuid == null || boundBeacons.isEmpty()) { + log.warn("[buildBeaconConfig] 区域 {} 绑定了 BEACON 设备但缺少 beaconUuid/major/minor 配置", areaId); + return null; + } + + return InspectionBeaconConfigVO.builder() + .uuid(uuid) + .boundBeacons(boundBeacons) + .rssiThreshold(rssiThreshold) + .timeoutMs(DEFAULT_TIMEOUT_MS) + .build(); + } + + /** + * 从 configData 中提取 beaconPresence 节点 + */ + @SuppressWarnings("unchecked") + private Map extractBeaconPresenceMap(Map configData) { + if (configData == null || !configData.containsKey("beaconPresence")) { + return null; + } + Object value = configData.get("beaconPresence"); + if (value instanceof Map) { + return (Map) value; + } + log.warn("[extractBeaconPresenceMap] beaconPresence 类型不是 Map: {}", value.getClass().getName()); + return null; + } + + private static Integer toInteger(Object value) { + if (value instanceof Integer) { + return (Integer) value; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return null; } private void validateTemplateExists(Long id) { diff --git a/viewsh-module-ops/viewsh-module-ops-api/src/main/java/com/viewsh/module/ops/enums/ErrorCodeConstants.java b/viewsh-module-ops/viewsh-module-ops-api/src/main/java/com/viewsh/module/ops/enums/ErrorCodeConstants.java index 2204d69..2c2359a 100644 --- a/viewsh-module-ops/viewsh-module-ops-api/src/main/java/com/viewsh/module/ops/enums/ErrorCodeConstants.java +++ b/viewsh-module-ops/viewsh-module-ops-api/src/main/java/com/viewsh/module/ops/enums/ErrorCodeConstants.java @@ -29,8 +29,9 @@ public interface ErrorCodeConstants { ErrorCode DEVICE_RELATION_NOT_FOUND = new ErrorCode(1_020_002_003, "设备关联关系不存在"); ErrorCode IOT_SERVICE_UNAVAILABLE = new ErrorCode(1_020_002_004, "IoT 设备服务不可用,请稍后重试"); - // ========== 巡检模块 1-020-003-000 ============ - ErrorCode INSPECTION_TEMPLATE_NOT_FOUND = new ErrorCode(1_020_003_000, "巡检模板不存在"); - ErrorCode INSPECTION_RECORD_NOT_FOUND = new ErrorCode(1_020_003_001, "巡检记录不存在"); + // ========== 巡检模块 1-020-004-000 ============ + ErrorCode INSPECTION_TEMPLATE_NOT_FOUND = new ErrorCode(1_020_004_000, "巡检模板不存在"); + ErrorCode INSPECTION_RECORD_NOT_FOUND = new ErrorCode(1_020_004_001, "巡检记录不存在"); + ErrorCode INSPECTION_AREA_NOT_ACTIVE = new ErrorCode(1_020_004_002, "该区域未启用,无法巡检"); } diff --git a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/dal/mysql/area/OpsBusAreaMapper.java b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/dal/mysql/area/OpsBusAreaMapper.java index 6f026c5..f79ed14 100644 --- a/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/dal/mysql/area/OpsBusAreaMapper.java +++ b/viewsh-module-ops/viewsh-module-ops-biz/src/main/java/com/viewsh/module/ops/dal/mysql/area/OpsBusAreaMapper.java @@ -6,7 +6,8 @@ import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO; import com.viewsh.module.ops.dal.dataobject.vo.area.OpsBusAreaPageReqVO; import org.apache.ibatis.annotations.Mapper; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; /** * 业务区域 Mapper @@ -103,4 +104,91 @@ public interface OpsBusAreaMapper extends BaseMapperX { return selectCount(OpsBusAreaDO::getParentId, parentId); } + // ==================== 区域全路径名称 ==================== + + /** + * 拼接区域全路径名称(如 "A园区/1号楼/2层/男卫") + * + * @param area 区域对象 + * @return 全路径名称 + */ + default String buildAreaFullName(OpsBusAreaDO area) { + if (area.getParentPath() == null || area.getParentPath().isEmpty()) { + return area.getAreaName(); + } + List ancestorIds = Arrays.stream(area.getParentPath().split("/")) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .collect(Collectors.toList()); + if (ancestorIds.isEmpty()) { + return area.getAreaName(); + } + Map nameMap = selectBatchIds(ancestorIds).stream() + .collect(Collectors.toMap(OpsBusAreaDO::getId, OpsBusAreaDO::getAreaName)); + StringBuilder sb = new StringBuilder(); + for (Long ancestorId : ancestorIds) { + String name = nameMap.get(ancestorId); + if (name != null) { + if (!sb.isEmpty()) { + sb.append("/"); + } + sb.append(name); + } + } + sb.append("/").append(area.getAreaName()); + return sb.toString(); + } + + /** + * 批量构建区域全路径名称(优化 N+1 查询,一次性查出所有祖先节点) + * + * @param areas 区域列表 + * @return area.id → 全路径名称 + */ + default Map buildAreaFullNameMap(Collection areas) { + if (areas == null || areas.isEmpty()) { + return Collections.emptyMap(); + } + // 收集所有祖先 ID,一次性批量查询 + Set allAncestorIds = new HashSet<>(); + for (OpsBusAreaDO area : areas) { + if (area.getParentPath() != null && !area.getParentPath().isEmpty()) { + Arrays.stream(area.getParentPath().split("/")) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .forEach(allAncestorIds::add); + } + } + Map ancestorNameMap = allAncestorIds.isEmpty() + ? Collections.emptyMap() + : selectBatchIds(allAncestorIds).stream() + .collect(Collectors.toMap(OpsBusAreaDO::getId, OpsBusAreaDO::getAreaName)); + + // 拼装每个区域的全路径 + Map result = new HashMap<>(areas.size()); + for (OpsBusAreaDO area : areas) { + if (area.getParentPath() == null || area.getParentPath().isEmpty()) { + result.put(area.getId(), area.getAreaName()); + continue; + } + List ancestorIds = Arrays.stream(area.getParentPath().split("/")) + .filter(s -> !s.isEmpty()) + .map(Long::valueOf) + .toList(); + StringBuilder sb = new StringBuilder(); + for (Long ancestorId : ancestorIds) { + String name = ancestorNameMap.get(ancestorId); + if (name != null) { + if (!sb.isEmpty()) { + sb.append("/"); + } + sb.append(name); + } + } + sb.append("/").append(area.getAreaName()); + result.put(area.getId(), sb.toString()); + } + return result; + } + } diff --git a/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionController.java b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionController.java index 40eebcf..7186f2f 100644 --- a/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionController.java +++ b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionController.java @@ -2,11 +2,8 @@ package com.viewsh.module.ops.controller.admin.inspection; import com.viewsh.framework.common.pojo.CommonResult; import com.viewsh.framework.common.pojo.PageResult; -import com.viewsh.framework.common.util.object.BeanUtils; import com.viewsh.framework.security.core.util.SecurityFrameworkUtils; import com.viewsh.module.ops.environment.controller.admin.inspection.vo.*; -import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO; -import com.viewsh.module.ops.environment.service.inspection.InspectionLocationService; import com.viewsh.module.ops.environment.service.inspection.InspectionRecordService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -17,8 +14,6 @@ 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 com.viewsh.framework.common.pojo.CommonResult.success; /** @@ -30,36 +25,30 @@ import static com.viewsh.framework.common.pojo.CommonResult.success; @Validated public class InspectionController { - @Resource - private InspectionLocationService inspectionLocationService; - @Resource private InspectionRecordService inspectionRecordService; - @PostMapping("/verify-location") - @Operation(summary = "蓝牙位置校验") - @Parameter(name = "areaId", description = "区域ID", required = true) - @PreAuthorize("@ss.hasPermission('ops:inspection:create')") - public CommonResult verifyLocation( - @RequestParam("areaId") Long areaId, - @Valid @RequestBody List detectedBeacons) { - return success(inspectionLocationService.verifyLocation(areaId, detectedBeacons)); - } - @PostMapping("/submit") @Operation(summary = "提交巡检结果") - @PreAuthorize("@ss.hasPermission('ops:inspection:create')") + @PreAuthorize("@ss.hasPermission('ops:inspection:create') and @ss.hasRole('inspector')") public CommonResult submitInspection(@Valid @RequestBody InspectionSubmitReqVO submitReqVO) { Long inspectorId = SecurityFrameworkUtils.getLoginUserId(); return success(inspectionRecordService.submitInspection(submitReqVO, inspectorId)); } + @GetMapping("/record/get") + @Operation(summary = "获得巡检记录详情") + @Parameter(name = "id", description = "巡检记录ID", required = true) + @PreAuthorize("@ss.hasPermission('ops:inspection:query')") + public CommonResult getRecord(@RequestParam("id") Long id) { + return success(inspectionRecordService.getRecordDetail(id)); + } + @GetMapping("/record/page") @Operation(summary = "获得巡检记录分页") @PreAuthorize("@ss.hasPermission('ops:inspection:query')") public CommonResult> getRecordPage(@Valid InspectionRecordPageReqVO pageReqVO) { - PageResult pageResult = inspectionRecordService.getRecordPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InspectionRecordRespVO.class)); + return success(inspectionRecordService.getRecordPage(pageReqVO)); } @GetMapping("/record/stats") diff --git a/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionTemplateController.java b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionTemplateController.java index 09418a4..42f73ad 100644 --- a/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionTemplateController.java +++ b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionTemplateController.java @@ -3,6 +3,7 @@ package com.viewsh.module.ops.controller.admin.inspection; import com.viewsh.framework.common.pojo.CommonResult; import com.viewsh.framework.common.pojo.PageResult; import com.viewsh.framework.common.util.object.BeanUtils; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionFormRespVO; import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplatePageReqVO; import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplateRespVO; import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplateSaveReqVO; @@ -75,12 +76,11 @@ public class InspectionTemplateController { } @GetMapping("/list-by-area") - @Operation(summary = "按区域获取巡检检查项列表(动态表单)") + @Operation(summary = "按区域获取巡检表单(扫码后调用)") @Parameter(name = "areaId", description = "区域ID", required = true) - @PreAuthorize("@ss.hasPermission('ops:inspection-template:query')") - public CommonResult> getTemplateListByArea(@RequestParam("areaId") Long areaId) { - List list = inspectionTemplateService.getTemplateListByAreaId(areaId); - return success(BeanUtils.toBean(list, InspectionTemplateRespVO.class)); + @PreAuthorize("@ss.hasPermission('ops:inspection-template:query') and @ss.hasRole('inspector')") + public CommonResult getInspectionFormByArea(@RequestParam("areaId") Long areaId) { + return success(inspectionTemplateService.getInspectionFormByAreaId(areaId)); } } diff --git a/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/framework/rpc/config/RpcConfiguration.java b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/framework/rpc/config/RpcConfiguration.java index a9b6bc9..5e0ba65 100644 --- a/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/framework/rpc/config/RpcConfiguration.java +++ b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/framework/rpc/config/RpcConfiguration.java @@ -5,12 +5,14 @@ import com.viewsh.module.iot.api.device.IotDeviceControlApi; import com.viewsh.module.iot.api.device.IotDeviceQueryApi; import com.viewsh.module.iot.api.device.IotDeviceStatusQueryApi; import com.viewsh.module.system.api.notify.NotifyMessageSendApi; +import com.viewsh.module.system.api.user.AdminUserApi; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Configuration; @Configuration(value = "opsRpcConfiguration", proxyBeanMethods = false) @EnableFeignClients(clients = { NotifyMessageSendApi.class, + AdminUserApi.class, IotDeviceControlApi.class, IotDeviceQueryApi.class, IotDeviceStatusQueryApi.class,