From fa45d942473705af03150292c75dbac4503fca27 Mon Sep 17 00:00:00 2001 From: lzh Date: Thu, 5 Mar 2026 17:07:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(ops):=20=E8=93=9D=E7=89=99=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E6=A0=A1=E9=AA=8C=E6=9C=8D=E5=8A=A1=E5=8F=8A=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=88Task=204=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .ralph/fix_plan.md | 7 +- .../admin/inspection/vo/DetectedBeaconVO.java | 20 ++++ .../inspection/vo/LocationVerifyResultVO.java | 28 +++++ .../inspection/InspectionLocationService.java | 25 +++++ .../InspectionLocationServiceImpl.java | 100 ++++++++++++++++++ .../inspection/InspectionController.java | 42 ++++++++ 6 files changed, 220 insertions(+), 2 deletions(-) create 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/LocationVerifyResultVO.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java create mode 100644 viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationServiceImpl.java create mode 100644 viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionController.java diff --git a/.ralph/fix_plan.md b/.ralph/fix_plan.md index 83457ef..1312f77 100644 --- a/.ralph/fix_plan.md +++ b/.ralph/fix_plan.md @@ -26,12 +26,14 @@ - 逻辑: area_id → 查 ops_bus_area 获取 function_type → 查 ops_inspection_template 加载检查项 - 返回: 检查项列表 -### 4. 蓝牙位置校验逻辑 -- [ ] InspectionLocationService(位置校验服务) +### 4. 蓝牙位置校验逻辑 ✅ +- [x] InspectionLocationService(位置校验服务) - 入参: area_id + detected_beacons - 查询 ops_area_device_relation 获取该区域绑定的信标列表 - 匹配算法: 至少1个绑定信标匹配,且 RSSI > 阈值 - 返回: 校验通过/失败 +- [x] InspectionController + verify-location 端点 +- [x] DetectedBeaconVO, LocationVerifyResultVO ## Medium Priority @@ -72,6 +74,7 @@ - [x] Task 1: 数据库表 + DO + Mapper(3表、3DO、3Mapper) - [x] Task 2: 巡检模板 CRUD(Service + Impl + Controller + VOs) - [x] Task 3: 获取动态表单接口(list-by-area,area_id → function_type → template 查询链路) +- [x] Task 4: 蓝牙位置校验(InspectionLocationService + verify-location 端点) ## Notes - 巡检是保洁业务线内的子功能,代码放 viewsh-module-environment-biz 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 new file mode 100644 index 0000000..d828f92 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/DetectedBeaconVO.java @@ -0,0 +1,20 @@ +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/LocationVerifyResultVO.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/LocationVerifyResultVO.java new file mode 100644 index 0000000..4a466f9 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/controller/admin/inspection/vo/LocationVerifyResultVO.java @@ -0,0 +1,28 @@ +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/service/inspection/InspectionLocationService.java b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java new file mode 100644 index 0000000..f2edb10 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationService.java @@ -0,0 +1,25 @@ +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 new file mode 100644 index 0000000..c46bbd9 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-environment-biz/src/main/java/com/viewsh/module/ops/environment/service/inspection/InspectionLocationServiceImpl.java @@ -0,0 +1,100 @@ +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-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 new file mode 100644 index 0000000..7e8b693 --- /dev/null +++ b/viewsh-module-ops/viewsh-module-ops-server/src/main/java/com/viewsh/module/ops/controller/admin/inspection/InspectionController.java @@ -0,0 +1,42 @@ +package com.viewsh.module.ops.controller.admin.inspection; + +import com.viewsh.framework.common.pojo.CommonResult; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.DetectedBeaconVO; +import com.viewsh.module.ops.environment.controller.admin.inspection.vo.LocationVerifyResultVO; +import com.viewsh.module.ops.environment.service.inspection.InspectionLocationService; +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 com.viewsh.framework.common.pojo.CommonResult.success; + +/** + * 管理后台 - 巡检 Controller + */ +@Tag(name = "管理后台 - 巡检") +@RestController +@RequestMapping("/ops/inspection") +@Validated +public class InspectionController { + + @Resource + private InspectionLocationService inspectionLocationService; + + @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)); + } + +}