Compare commits
11 Commits
master
...
feature/cl
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ce1561437 | |||
| 01e9a556ab | |||
| e3882e1c2f | |||
| f70402587d | |||
| 743875e65e | |||
| 23cf3b62b2 | |||
| 162bf1d20d | |||
| 3120b1911d | |||
| e4dde8dbc1 | |||
| fa45d94247 | |||
| 99651be386 |
84
.ralph/fix_plan.md
Normal file
84
.ralph/fix_plan.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Ralph Fix Plan - 保洁巡检模块
|
||||
|
||||
## 需求文档
|
||||
参考: docs/物业服务AIoT平台-保洁服务模块.md(模块二:巡检小程序后端)
|
||||
|
||||
## High Priority
|
||||
|
||||
### 1. 数据库表 + DO + Mapper ✅
|
||||
- [x] 创建 `ops_inspection_template` 表及 SQL(巡检检查项模板)
|
||||
- [x] 创建 `ops_inspection_record` 表及 SQL(巡检主记录)
|
||||
- [x] 创建 `ops_inspection_record_item` 表及 SQL(巡检明细)
|
||||
- [x] 创建对应 DO 类: OpsInspectionTemplateDO, OpsInspectionRecordDO, OpsInspectionRecordItemDO
|
||||
- [x] 创建对应 Mapper: OpsInspectionTemplateMapper, OpsInspectionRecordMapper, OpsInspectionRecordItemMapper
|
||||
- SQL: sql/mysql/aiot_ops_inspection.sql
|
||||
- DO: environment-biz/dal/dataobject/inspection/
|
||||
- Mapper: environment-biz/dal/mysql/inspection/
|
||||
|
||||
### 2. 巡检模板 CRUD(管理后台配置检查项) ✅
|
||||
- [x] InspectionTemplateService 接口 + Impl(增删改查,按 function_type 查询)
|
||||
- [x] InspectionTemplateController(管理端 REST API)
|
||||
- [x] 请求/响应 VO 类
|
||||
|
||||
### 3. 获取动态表单接口 ✅
|
||||
- [x] GET /admin-api/ops/inspection/template/list-by-area
|
||||
- 入参: area_id + detected_beacons(蓝牙指纹数组)
|
||||
- 逻辑: area_id → 查 ops_bus_area 获取 function_type → 查 ops_inspection_template 加载检查项
|
||||
- 返回: 检查项列表
|
||||
|
||||
### 4. 蓝牙位置校验逻辑 ✅
|
||||
- [x] InspectionLocationService(位置校验服务)
|
||||
- 入参: area_id + detected_beacons
|
||||
- 查询 ops_area_device_relation 获取该区域绑定的信标列表
|
||||
- 匹配算法: 至少1个绑定信标匹配,且 RSSI > 阈值
|
||||
- 返回: 校验通过/失败
|
||||
- [x] InspectionController + verify-location 端点
|
||||
- [x] DetectedBeaconVO, LocationVerifyResultVO
|
||||
|
||||
## Medium Priority
|
||||
|
||||
### 5. 提交巡检结果接口
|
||||
- [ ] POST /admin-api/ops/inspection/submit
|
||||
- 入参: area_id, is_location_exception, items[{template_id, is_passed, remark}]
|
||||
- 逻辑:
|
||||
1. 保存 ops_inspection_record 主记录
|
||||
2. 批量保存 ops_inspection_record_item 明细
|
||||
3. 判定 result_status: 任意一项 is_passed=false → 不合格
|
||||
4. 不合格时触发异步归属判定
|
||||
- [ ] InspectionRecordService 接口 + Impl
|
||||
- [ ] InspectionController(巡检提交 REST API)
|
||||
|
||||
### 6. 归属判定异步逻辑
|
||||
- [ ] InspectionAttributionService(归属判定服务)
|
||||
- 以巡检提交时间为基点,回溯该区域上一个 COMPLETED 工单
|
||||
- 获取保洁员实际停留时长 T_stay(来自 ops_order.actual_duration 或工单扩展)
|
||||
- 获取区域标准时长 clean_threshold(来自 ops_bus_area.standard_duration)
|
||||
- 判定:
|
||||
- T_stay >= clean_threshold → attribution_result=2(突发状况),不扣分
|
||||
- T_stay < clean_threshold → attribution_result=1(个人责任),扣信用分
|
||||
- 更新 ops_inspection_record 的 attribution_result, last_order_id, stay_duration
|
||||
|
||||
### 7. 整改工单自动创建
|
||||
- [ ] 不合格巡检触发创建整改派单
|
||||
- 调用现有 OpsOrderService / OrderLifecycleManager 创建工单
|
||||
- source_type = INSPECTION
|
||||
- 关联 generated_order_id 回写到 ops_inspection_record
|
||||
- 对接工单引擎的优先级和派单逻辑
|
||||
|
||||
## Low Priority
|
||||
- [ ] 巡检记录查询接口(分页、按区域/时间筛选)
|
||||
- [ ] 巡检统计接口(合格率、不合格热点区域)
|
||||
|
||||
## Completed
|
||||
- [x] 需求分析与表结构设计
|
||||
- [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
|
||||
- 复用 ops_area_device_relation 做蓝牙信标校验,不新建 iot_device_location
|
||||
- 复用 ops_bus_area.standard_duration 作为 clean_threshold,不新建 ops_rule_config
|
||||
- Controller 放在 ops-server 模块的 controller/admin/inspection/ 下
|
||||
- 遵循现有 CleanOrderService 的编码模式
|
||||
71
sql/mysql/aiot_ops_inspection.sql
Normal file
71
sql/mysql/aiot_ops_inspection.sql
Normal file
@@ -0,0 +1,71 @@
|
||||
-- =============================================
|
||||
-- 巡检模块表结构
|
||||
-- =============================================
|
||||
|
||||
-- ----------------------------
|
||||
-- 巡检检查项模板表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `ops_inspection_template`;
|
||||
CREATE TABLE `ops_inspection_template` (
|
||||
`id` bigint NOT NULL COMMENT '模板ID',
|
||||
`function_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '功能类型(关联 ops_bus_area.function_type)',
|
||||
`item_title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '检查项标题',
|
||||
`sort_order` int NOT NULL DEFAULT 0 COMMENT '排序序号',
|
||||
`is_active` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否启用',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_function_type` (`function_type`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检检查项模板';
|
||||
|
||||
-- ----------------------------
|
||||
-- 巡检主记录表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `ops_inspection_record`;
|
||||
CREATE TABLE `ops_inspection_record` (
|
||||
`id` bigint NOT NULL COMMENT '巡检记录ID',
|
||||
`area_id` bigint NOT NULL COMMENT '区域ID',
|
||||
`inspector_id` bigint NOT NULL COMMENT '巡检员用户ID',
|
||||
`is_location_exception` tinyint NOT NULL DEFAULT 0 COMMENT '位置是否异常(0正常 1异常)',
|
||||
`result_status` tinyint COMMENT '巡检结果(0不合格 1合格)',
|
||||
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '备注',
|
||||
`last_order_id` bigint COMMENT '归属判定-上一个完成工单ID',
|
||||
`stay_duration` int COMMENT '归属判定-保洁员停留时长(秒)',
|
||||
`attribution_result` tinyint COMMENT '归属判定结果(1个人责任 2突发状况 3正常)',
|
||||
`generated_order_id` bigint COMMENT '整改工单ID',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_area_id` (`area_id`),
|
||||
KEY `idx_inspector_id` (`inspector_id`),
|
||||
KEY `idx_generated_order_id` (`generated_order_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检主记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- 巡检明细表
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `ops_inspection_record_item`;
|
||||
CREATE TABLE `ops_inspection_record_item` (
|
||||
`id` bigint NOT NULL COMMENT '明细ID',
|
||||
`record_id` bigint NOT NULL COMMENT '巡检记录ID',
|
||||
`template_id` bigint NOT NULL COMMENT '模板检查项ID',
|
||||
`is_passed` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否合格',
|
||||
`remark` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '备注',
|
||||
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '创建者',
|
||||
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '更新者',
|
||||
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_record_id` (`record_id`),
|
||||
KEY `idx_template_id` (`template_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检明细';
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.vo;
|
||||
|
||||
import com.viewsh.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.viewsh.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检记录分页查询 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class InspectionRecordPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "区域ID", example = "1")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "巡检员用户ID", example = "100")
|
||||
private Long inspectorId;
|
||||
|
||||
@Schema(description = "巡检结果(0不合格 1合格)", example = "1")
|
||||
private Integer resultStatus;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检记录 Response VO")
|
||||
@Data
|
||||
public class InspectionRecordRespVO {
|
||||
|
||||
@Schema(description = "巡检记录ID", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "区域ID", example = "1")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "巡检员用户ID", example = "100")
|
||||
private Long inspectorId;
|
||||
|
||||
@Schema(description = "位置是否异常(0正常 1异常)", example = "0")
|
||||
private Integer isLocationException;
|
||||
|
||||
@Schema(description = "巡检结果(0不合格 1合格)", example = "1")
|
||||
private Integer resultStatus;
|
||||
|
||||
@Schema(description = "备注", example = "检查完成")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "归属判定结果(1个人责任 2突发状况 3正常)", example = "3")
|
||||
private Integer attributionResult;
|
||||
|
||||
@Schema(description = "整改工单ID", example = "2048")
|
||||
private Long generatedOrderId;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.viewsh.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检统计查询 Request VO")
|
||||
@Data
|
||||
public class InspectionStatsReqVO {
|
||||
|
||||
@Schema(description = "区域ID(可选,不传则统计全部区域)", example = "1")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "时间范围")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
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;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检统计 Response VO")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class InspectionStatsRespVO {
|
||||
|
||||
@Schema(description = "总巡检次数", example = "100")
|
||||
private Long totalCount;
|
||||
|
||||
@Schema(description = "合格次数", example = "85")
|
||||
private Long passedCount;
|
||||
|
||||
@Schema(description = "不合格次数", example = "15")
|
||||
private Long failedCount;
|
||||
|
||||
@Schema(description = "合格率(百分比)", example = "85.00")
|
||||
private Double passRate;
|
||||
|
||||
@Schema(description = "不合格热点区域(按不合格次数降序)")
|
||||
private List<AreaFailStat> hotSpotAreas;
|
||||
|
||||
@Schema(description = "区域不合格统计")
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class AreaFailStat {
|
||||
|
||||
@Schema(description = "区域ID", example = "1")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "不合格次数", example = "5")
|
||||
private Long failedCount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检提交明细项 VO")
|
||||
@Data
|
||||
public class InspectionSubmitItemVO {
|
||||
|
||||
@Schema(description = "模板检查项ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "模板检查项ID不能为空")
|
||||
private Long templateId;
|
||||
|
||||
@Schema(description = "是否合格", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否合格不能为空")
|
||||
private Boolean isPassed;
|
||||
|
||||
@Schema(description = "备注", example = "地面有明显污渍")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.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;
|
||||
|
||||
@Schema(description = "管理后台 - 提交巡检结果 Request VO")
|
||||
@Data
|
||||
public class InspectionSubmitReqVO {
|
||||
|
||||
@Schema(description = "区域ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "区域ID不能为空")
|
||||
private Long areaId;
|
||||
|
||||
@Schema(description = "位置是否异常(0正常 1异常)", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "位置异常标志不能为空")
|
||||
private Integer isLocationException;
|
||||
|
||||
@Schema(description = "备注", example = "卫生间地面有污渍")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "巡检明细项列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotEmpty(message = "巡检明细项不能为空")
|
||||
@Valid
|
||||
private List<InspectionSubmitItemVO> items;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.vo;
|
||||
|
||||
import com.viewsh.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检模板分页查询 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class InspectionTemplatePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "功能类型", example = "TOILET")
|
||||
private String functionType;
|
||||
|
||||
@Schema(description = "是否启用", example = "true")
|
||||
private Boolean isActive;
|
||||
|
||||
@Schema(description = "检查项标题(模糊搜索)", example = "地面")
|
||||
private String itemTitle;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.viewsh.module.ops.environment.controller.admin.inspection.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 巡检模板 Response VO")
|
||||
@Data
|
||||
public class InspectionTemplateRespVO {
|
||||
|
||||
@Schema(description = "模板ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "功能类型", example = "TOILET")
|
||||
private String functionType;
|
||||
|
||||
@Schema(description = "检查项标题", example = "地面是否干净")
|
||||
private String itemTitle;
|
||||
|
||||
@Schema(description = "排序序号", example = "1")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "是否启用", example = "true")
|
||||
private Boolean isActive;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
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 = "管理后台 - 巡检模板创建/修改 Request VO")
|
||||
@Data
|
||||
public class InspectionTemplateSaveReqVO {
|
||||
|
||||
@Schema(description = "模板ID(修改时必填)", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "功能类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "TOILET")
|
||||
@NotBlank(message = "功能类型不能为空")
|
||||
private String functionType;
|
||||
|
||||
@Schema(description = "检查项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "地面是否干净")
|
||||
@NotBlank(message = "检查项标题不能为空")
|
||||
private String itemTitle;
|
||||
|
||||
@Schema(description = "排序序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "排序序号不能为空")
|
||||
private Integer sortOrder;
|
||||
|
||||
@Schema(description = "是否启用", example = "true")
|
||||
private Boolean isActive;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.viewsh.module.ops.environment.dal.dataobject.inspection;
|
||||
|
||||
import com.viewsh.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.*;
|
||||
|
||||
/**
|
||||
* 巡检主记录 DO
|
||||
*/
|
||||
@TableName("ops_inspection_record")
|
||||
@KeySequence("ops_inspection_record_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OpsInspectionRecordDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 巡检记录ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 区域ID
|
||||
*/
|
||||
private Long areaId;
|
||||
/**
|
||||
* 巡检员用户ID
|
||||
*/
|
||||
private Long inspectorId;
|
||||
/**
|
||||
* 位置是否异常(0正常 1异常)
|
||||
*/
|
||||
private Integer isLocationException;
|
||||
/**
|
||||
* 巡检结果(0不合格 1合格)
|
||||
*/
|
||||
private Integer resultStatus;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
/**
|
||||
* 归属判定-上一个完成工单ID
|
||||
*/
|
||||
private Long lastOrderId;
|
||||
/**
|
||||
* 归属判定-保洁员停留时长(秒)
|
||||
*/
|
||||
private Integer stayDuration;
|
||||
/**
|
||||
* 归属判定结果(1个人责任 2突发状况 3正常)
|
||||
*/
|
||||
private Integer attributionResult;
|
||||
/**
|
||||
* 整改工单ID
|
||||
*/
|
||||
private Long generatedOrderId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.viewsh.module.ops.environment.dal.dataobject.inspection;
|
||||
|
||||
import com.viewsh.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.*;
|
||||
|
||||
/**
|
||||
* 巡检明细 DO
|
||||
*/
|
||||
@TableName("ops_inspection_record_item")
|
||||
@KeySequence("ops_inspection_record_item_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OpsInspectionRecordItemDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 明细ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 巡检记录ID
|
||||
*/
|
||||
private Long recordId;
|
||||
/**
|
||||
* 模板检查项ID
|
||||
*/
|
||||
private Long templateId;
|
||||
/**
|
||||
* 是否合格
|
||||
*/
|
||||
private Boolean isPassed;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.viewsh.module.ops.environment.dal.dataobject.inspection;
|
||||
|
||||
import com.viewsh.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.*;
|
||||
|
||||
/**
|
||||
* 巡检检查项模板 DO
|
||||
*/
|
||||
@TableName("ops_inspection_template")
|
||||
@KeySequence("ops_inspection_template_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class OpsInspectionTemplateDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 模板ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 功能类型(关联 ops_bus_area.function_type)
|
||||
*/
|
||||
private String functionType;
|
||||
/**
|
||||
* 检查项标题
|
||||
*/
|
||||
private String itemTitle;
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
private Integer sortOrder;
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private Boolean isActive;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.viewsh.module.ops.environment.dal.mysql.inspection;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordItemDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 巡检明细 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface OpsInspectionRecordItemMapper extends BaseMapperX<OpsInspectionRecordItemDO> {
|
||||
|
||||
/**
|
||||
* 按巡检记录ID查询明细
|
||||
*/
|
||||
default List<OpsInspectionRecordItemDO> selectListByRecordId(Long recordId) {
|
||||
return selectList(OpsInspectionRecordItemDO::getRecordId, recordId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.viewsh.module.ops.environment.dal.mysql.inspection;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 巡检主记录 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface OpsInspectionRecordMapper extends BaseMapperX<OpsInspectionRecordDO> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.viewsh.module.ops.environment.dal.mysql.inspection;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionTemplateDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 巡检检查项模板 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface OpsInspectionTemplateMapper extends BaseMapperX<OpsInspectionTemplateDO> {
|
||||
|
||||
/**
|
||||
* 按功能类型查询启用的检查项,按排序序号升序
|
||||
*/
|
||||
default List<OpsInspectionTemplateDO> selectListByFunctionType(String functionType) {
|
||||
return selectList(new LambdaQueryWrapperX<OpsInspectionTemplateDO>()
|
||||
.eq(OpsInspectionTemplateDO::getFunctionType, functionType)
|
||||
.eq(OpsInspectionTemplateDO::getIsActive, true)
|
||||
.orderByAsc(OpsInspectionTemplateDO::getSortOrder));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 巡检异步处理器(独立 Bean,解决 @Async 自调用问题)
|
||||
*
|
||||
* 负责在事务提交后异步执行:
|
||||
* 1. 归属判定
|
||||
* 2. 整改工单创建
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class InspectionAsyncHandler {
|
||||
|
||||
@Resource
|
||||
private InspectionAttributionService inspectionAttributionService;
|
||||
|
||||
@Resource
|
||||
private InspectionRectificationService inspectionRectificationService;
|
||||
|
||||
/**
|
||||
* 异步处理不合格巡检的后续逻辑
|
||||
*/
|
||||
@Async("ops-task-executor")
|
||||
public void handleFailedInspection(Long recordId, Long areaId) {
|
||||
// 1. 归属判定
|
||||
try {
|
||||
inspectionAttributionService.determineAttribution(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[handleFailedInspection] 归属判定异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
|
||||
// 2. 创建整改工单(不论归属判定结果,不合格即需整改)
|
||||
try {
|
||||
inspectionRectificationService.createRectificationOrder(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[handleFailedInspection] 整改工单创建异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
/**
|
||||
* 巡检归属判定 Service 接口
|
||||
*
|
||||
* 当巡检结果为不合格时,异步执行归属判定:
|
||||
* 1. 回溯该区域最近一个 COMPLETED 工单
|
||||
* 2. 比较保洁员实际停留时长与区域标准时长
|
||||
* 3. 判定责任归属并回写巡检记录
|
||||
*/
|
||||
public interface InspectionAttributionService {
|
||||
|
||||
/**
|
||||
* 执行归属判定
|
||||
*
|
||||
* @param recordId 巡检记录ID
|
||||
* @param areaId 区域ID
|
||||
*/
|
||||
void determineAttribution(Long recordId, Long areaId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
import com.viewsh.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO;
|
||||
import com.viewsh.module.ops.dal.dataobject.workorder.OpsOrderDO;
|
||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||
import com.viewsh.module.ops.dal.mysql.workorder.OpsOrderMapper;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO;
|
||||
import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordMapper;
|
||||
import com.viewsh.module.ops.enums.WorkOrderStatusEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* 巡检归属判定 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class InspectionAttributionServiceImpl implements InspectionAttributionService {
|
||||
|
||||
/** 归属判定:个人责任(停留时长不足) */
|
||||
private static final int ATTRIBUTION_PERSONAL = 1;
|
||||
/** 归属判定:突发状况(停留时长达标) */
|
||||
private static final int ATTRIBUTION_EMERGENCY = 2;
|
||||
/** 归属判定:正常(合格巡检,无需判定) */
|
||||
private static final int ATTRIBUTION_NORMAL = 3;
|
||||
|
||||
@Resource
|
||||
private OpsInspectionRecordMapper inspectionRecordMapper;
|
||||
|
||||
@Resource
|
||||
private OpsOrderMapper opsOrderMapper;
|
||||
|
||||
@Resource
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void determineAttribution(Long recordId, Long areaId) {
|
||||
// 1. 回溯该区域最近一个 COMPLETED 工单
|
||||
OpsOrderDO lastOrder = opsOrderMapper.selectOne(new LambdaQueryWrapperX<OpsOrderDO>()
|
||||
.eq(OpsOrderDO::getAreaId, areaId)
|
||||
.eq(OpsOrderDO::getStatus, WorkOrderStatusEnum.COMPLETED.getStatus())
|
||||
.orderByDesc(OpsOrderDO::getEndTime)
|
||||
.last("LIMIT 1"));
|
||||
|
||||
if (lastOrder == null) {
|
||||
log.warn("[determineAttribution] 区域 {} 无已完成工单,标记为正常: recordId={}", areaId, recordId);
|
||||
updateAttributionResult(recordId, null, null, ATTRIBUTION_NORMAL);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 获取保洁员实际停留时长(秒)
|
||||
// completionSeconds 记录的是工单从开始到完成的耗时,即实际作业时长
|
||||
Integer stayDurationSeconds = lastOrder.getCompletionSeconds();
|
||||
if (stayDurationSeconds == null) {
|
||||
stayDurationSeconds = 0;
|
||||
}
|
||||
|
||||
// 3. 获取区域标准时长(standardDuration 单位为分钟,转换为秒)
|
||||
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
|
||||
if (area == null || area.getStandardDuration() == null) {
|
||||
log.warn("[determineAttribution] 区域 {} 无标准时长配置,标记为正常: recordId={}", areaId, recordId);
|
||||
updateAttributionResult(recordId, lastOrder.getId(), stayDurationSeconds, ATTRIBUTION_NORMAL);
|
||||
return;
|
||||
}
|
||||
int standardDurationSeconds = area.getStandardDuration() * 60; // 分钟 → 秒
|
||||
|
||||
// 4. 判定归属
|
||||
// T_stay >= clean_threshold → 突发状况(保洁员已做到位,不扣分)
|
||||
// T_stay < clean_threshold → 个人责任(保洁时长不足,扣信用分)
|
||||
int attributionResult = stayDurationSeconds >= standardDurationSeconds
|
||||
? ATTRIBUTION_EMERGENCY
|
||||
: ATTRIBUTION_PERSONAL;
|
||||
|
||||
log.info("[determineAttribution] 归属判定完成: recordId={}, areaId={}, lastOrderId={}, " +
|
||||
"stayDuration={}s, standardDuration={}s({}min), result={}",
|
||||
recordId, areaId, lastOrder.getId(),
|
||||
stayDurationSeconds, standardDurationSeconds, area.getStandardDuration(), attributionResult);
|
||||
|
||||
// 5. 回写巡检记录
|
||||
updateAttributionResult(recordId, lastOrder.getId(), stayDurationSeconds, attributionResult);
|
||||
}
|
||||
|
||||
private void updateAttributionResult(Long recordId, Long lastOrderId,
|
||||
Integer stayDuration, int attributionResult) {
|
||||
OpsInspectionRecordDO update = new OpsInspectionRecordDO();
|
||||
update.setId(recordId);
|
||||
update.setLastOrderId(lastOrderId);
|
||||
update.setStayDuration(stayDuration);
|
||||
update.setAttributionResult(attributionResult);
|
||||
inspectionRecordMapper.updateById(update);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
* 校验巡检人员是否在指定区域
|
||||
* <p>
|
||||
* 逻辑:查询该区域绑定的 BEACON 类型设备,
|
||||
* 匹配检测到的蓝牙信标列表,至少1个绑定信标匹配且 RSSI 大于阈值即通过
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @param detectedBeacons 检测到的蓝牙信标列表
|
||||
* @return 校验结果
|
||||
*/
|
||||
LocationVerifyResultVO verifyLocation(Long areaId, List<DetectedBeaconVO> detectedBeacons);
|
||||
|
||||
}
|
||||
@@ -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<DetectedBeaconVO> detectedBeacons) {
|
||||
if (detectedBeacons == null || detectedBeacons.isEmpty()) {
|
||||
return LocationVerifyResultVO.fail("未检测到蓝牙信标");
|
||||
}
|
||||
|
||||
// 查询该区域绑定的 BEACON 设备
|
||||
List<OpsAreaDeviceRelationDO> 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<String, Object> 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<String, Object> configData = relation.getConfigData();
|
||||
if (configData == null) {
|
||||
return DEFAULT_RSSI_THRESHOLD;
|
||||
}
|
||||
Object enter = configData.get("enter");
|
||||
if (enter instanceof Map) {
|
||||
Object threshold = ((Map<String, Object>) enter).get("rssiThreshold");
|
||||
if (threshold instanceof Number) {
|
||||
return ((Number) threshold).intValue();
|
||||
}
|
||||
}
|
||||
return DEFAULT_RSSI_THRESHOLD;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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.dal.dataobject.inspection.OpsInspectionRecordDO;
|
||||
|
||||
/**
|
||||
* 巡检记录 Service 接口
|
||||
*/
|
||||
public interface InspectionRecordService {
|
||||
|
||||
/**
|
||||
* 提交巡检结果
|
||||
*
|
||||
* @param submitReqVO 提交请求
|
||||
* @param inspectorId 巡检员用户ID
|
||||
* @return 巡检记录ID
|
||||
*/
|
||||
Long submitInspection(InspectionSubmitReqVO submitReqVO, Long inspectorId);
|
||||
|
||||
/**
|
||||
* 获得巡检记录分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 巡检记录分页
|
||||
*/
|
||||
PageResult<OpsInspectionRecordDO> getRecordPage(InspectionRecordPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得巡检统计(合格率、不合格热点区域)
|
||||
*
|
||||
* @param reqVO 统计查询条件
|
||||
* @return 巡检统计
|
||||
*/
|
||||
InspectionStatsRespVO getInspectionStats(InspectionStatsReqVO reqVO);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
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.mybatis.core.query.LambdaQueryWrapperX;
|
||||
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.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 org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 巡检记录 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class InspectionRecordServiceImpl implements InspectionRecordService {
|
||||
|
||||
/** 巡检结果:合格 */
|
||||
private static final int RESULT_STATUS_PASSED = 1;
|
||||
/** 巡检结果:不合格 */
|
||||
private static final int RESULT_STATUS_FAILED = 0;
|
||||
|
||||
@Resource
|
||||
private OpsInspectionRecordMapper inspectionRecordMapper;
|
||||
|
||||
@Resource
|
||||
private OpsInspectionRecordItemMapper inspectionRecordItemMapper;
|
||||
|
||||
@Resource
|
||||
private InspectionAsyncHandler inspectionAsyncHandler;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long submitInspection(InspectionSubmitReqVO submitReqVO, Long inspectorId) {
|
||||
// 1. 判定巡检结果:任一项不合格 → 整体不合格
|
||||
boolean allPassed = submitReqVO.getItems().stream()
|
||||
.allMatch(InspectionSubmitItemVO::getIsPassed);
|
||||
int resultStatus = allPassed ? RESULT_STATUS_PASSED : RESULT_STATUS_FAILED;
|
||||
|
||||
// 2. 保存巡检主记录
|
||||
OpsInspectionRecordDO record = OpsInspectionRecordDO.builder()
|
||||
.areaId(submitReqVO.getAreaId())
|
||||
.inspectorId(inspectorId)
|
||||
.isLocationException(submitReqVO.getIsLocationException())
|
||||
.resultStatus(resultStatus)
|
||||
.remark(submitReqVO.getRemark())
|
||||
.build();
|
||||
inspectionRecordMapper.insert(record);
|
||||
|
||||
// 3. 批量保存巡检明细
|
||||
List<OpsInspectionRecordItemDO> items = submitReqVO.getItems().stream()
|
||||
.map(itemVO -> OpsInspectionRecordItemDO.builder()
|
||||
.recordId(record.getId())
|
||||
.templateId(itemVO.getTemplateId())
|
||||
.isPassed(itemVO.getIsPassed())
|
||||
.remark(itemVO.getRemark())
|
||||
.build())
|
||||
.toList();
|
||||
inspectionRecordItemMapper.insertBatch(items);
|
||||
|
||||
// 4. 不合格时,在事务提交后异步触发归属判定 + 整改工单
|
||||
if (resultStatus == RESULT_STATUS_FAILED) {
|
||||
Long recordId = record.getId();
|
||||
Long areaId = submitReqVO.getAreaId();
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
inspectionAsyncHandler.handleFailedInspection(recordId, areaId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OpsInspectionRecordDO> getRecordPage(InspectionRecordPageReqVO pageReqVO) {
|
||||
return inspectionRecordMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<OpsInspectionRecordDO>()
|
||||
.eqIfPresent(OpsInspectionRecordDO::getAreaId, pageReqVO.getAreaId())
|
||||
.eqIfPresent(OpsInspectionRecordDO::getInspectorId, pageReqVO.getInspectorId())
|
||||
.eqIfPresent(OpsInspectionRecordDO::getResultStatus, pageReqVO.getResultStatus())
|
||||
.betweenIfPresent(OpsInspectionRecordDO::getCreateTime, pageReqVO.getCreateTime())
|
||||
.orderByDesc(OpsInspectionRecordDO::getId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InspectionStatsRespVO getInspectionStats(InspectionStatsReqVO reqVO) {
|
||||
// 1. 构建基础查询条件
|
||||
LambdaQueryWrapperX<OpsInspectionRecordDO> baseWrapper = new LambdaQueryWrapperX<OpsInspectionRecordDO>()
|
||||
.eqIfPresent(OpsInspectionRecordDO::getAreaId, reqVO.getAreaId())
|
||||
.betweenIfPresent(OpsInspectionRecordDO::getCreateTime, reqVO.getCreateTime());
|
||||
|
||||
// 2. 总数 & 合格数
|
||||
long totalCount = inspectionRecordMapper.selectCount(baseWrapper);
|
||||
long passedCount = inspectionRecordMapper.selectCount(new LambdaQueryWrapperX<OpsInspectionRecordDO>()
|
||||
.eqIfPresent(OpsInspectionRecordDO::getAreaId, reqVO.getAreaId())
|
||||
.betweenIfPresent(OpsInspectionRecordDO::getCreateTime, reqVO.getCreateTime())
|
||||
.eq(OpsInspectionRecordDO::getResultStatus, RESULT_STATUS_PASSED));
|
||||
long failedCount = totalCount - passedCount;
|
||||
|
||||
// 3. 合格率
|
||||
double passRate = totalCount > 0
|
||||
? BigDecimal.valueOf(passedCount * 100.0 / totalCount).setScale(2, RoundingMode.HALF_UP).doubleValue()
|
||||
: 0.0;
|
||||
|
||||
// 4. 不合格热点区域(按不合格次数降序,取前10)
|
||||
QueryWrapper<OpsInspectionRecordDO> groupWrapper = new QueryWrapper<>();
|
||||
groupWrapper.select("area_id AS areaId", "COUNT(*) AS failedCount");
|
||||
groupWrapper.eq("result_status", RESULT_STATUS_FAILED);
|
||||
groupWrapper.eq("deleted", false);
|
||||
if (reqVO.getAreaId() != null) {
|
||||
groupWrapper.eq("area_id", reqVO.getAreaId());
|
||||
}
|
||||
if (reqVO.getCreateTime() != null && reqVO.getCreateTime().length == 2) {
|
||||
groupWrapper.between("create_time", reqVO.getCreateTime()[0], reqVO.getCreateTime()[1]);
|
||||
}
|
||||
groupWrapper.groupBy("area_id");
|
||||
groupWrapper.orderByDesc("failedCount");
|
||||
groupWrapper.last("LIMIT 10");
|
||||
|
||||
List<Map<String, Object>> maps = inspectionRecordMapper.selectMaps(groupWrapper);
|
||||
List<InspectionStatsRespVO.AreaFailStat> hotSpots = maps.stream()
|
||||
.map(m -> InspectionStatsRespVO.AreaFailStat.builder()
|
||||
.areaId(((Number) m.get("areaId")).longValue())
|
||||
.failedCount(((Number) m.get("failedCount")).longValue())
|
||||
.build())
|
||||
.toList();
|
||||
|
||||
return InspectionStatsRespVO.builder()
|
||||
.totalCount(totalCount)
|
||||
.passedCount(passedCount)
|
||||
.failedCount(failedCount)
|
||||
.passRate(passRate)
|
||||
.hotSpotAreas(hotSpots)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
/**
|
||||
* 巡检整改工单 Service 接口
|
||||
*
|
||||
* 当巡检不合格时,自动创建整改工单:
|
||||
* - source_type = INSPECTION
|
||||
* - 对接现有保洁工单引擎
|
||||
* - 回写 generated_order_id 到巡检记录
|
||||
*/
|
||||
public interface InspectionRectificationService {
|
||||
|
||||
/**
|
||||
* 创建整改工单
|
||||
*
|
||||
* @param recordId 巡检记录ID
|
||||
* @param areaId 区域ID
|
||||
* @return 创建的工单ID
|
||||
*/
|
||||
Long createRectificationOrder(Long recordId, Long areaId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
import com.viewsh.module.ops.dal.dataobject.area.OpsBusAreaDO;
|
||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.CleanOrderAutoCreateReqDTO;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionRecordDO;
|
||||
import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordMapper;
|
||||
import com.viewsh.module.ops.environment.service.cleanorder.CleanOrderService;
|
||||
import com.viewsh.module.ops.enums.PriorityEnum;
|
||||
import com.viewsh.module.ops.enums.SourceTypeEnum;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
/**
|
||||
* 巡检整改工单 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class InspectionRectificationServiceImpl implements InspectionRectificationService {
|
||||
|
||||
/** 默认整改工单预计时长(分钟) */
|
||||
private static final int DEFAULT_RECTIFICATION_DURATION_MINUTES = 30;
|
||||
|
||||
@Resource
|
||||
private CleanOrderService cleanOrderService;
|
||||
|
||||
@Resource
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
|
||||
@Resource
|
||||
private OpsInspectionRecordMapper inspectionRecordMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createRectificationOrder(Long recordId, Long areaId) {
|
||||
// 1. 获取区域信息
|
||||
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
|
||||
String areaName = (area != null) ? area.getAreaName() : "未知区域";
|
||||
int expectedDuration = (area != null && area.getStandardDuration() != null)
|
||||
? area.getStandardDuration() : DEFAULT_RECTIFICATION_DURATION_MINUTES;
|
||||
|
||||
// 2. 构建整改工单请求
|
||||
CleanOrderAutoCreateReqDTO createReq = new CleanOrderAutoCreateReqDTO();
|
||||
createReq.setOrderType("CLEAN");
|
||||
createReq.setSourceType(SourceTypeEnum.INSPECTION.getType());
|
||||
createReq.setTitle("巡检整改:" + areaName);
|
||||
createReq.setDescription("巡检发现不合格,需重新清洁。巡检记录ID:" + recordId);
|
||||
createReq.setPriority(PriorityEnum.P1.getPriority());
|
||||
createReq.setAreaId(areaId);
|
||||
createReq.setExpectedDuration(expectedDuration);
|
||||
createReq.setCleaningType("SPOT");
|
||||
createReq.setDifficultyLevel(3);
|
||||
|
||||
// 3. 调用现有工单引擎创建工单
|
||||
Long orderId = cleanOrderService.createAutoCleanOrder(createReq);
|
||||
log.info("[createRectificationOrder] 整改工单创建成功: recordId={}, areaId={}, orderId={}",
|
||||
recordId, areaId, orderId);
|
||||
|
||||
// 4. 回写 generated_order_id 到巡检记录
|
||||
OpsInspectionRecordDO update = new OpsInspectionRecordDO();
|
||||
update.setId(recordId);
|
||||
update.setGeneratedOrderId(orderId);
|
||||
inspectionRecordMapper.updateById(update);
|
||||
|
||||
return orderId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
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.dal.dataobject.inspection.OpsInspectionTemplateDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 巡检检查项模板 Service
|
||||
*/
|
||||
public interface InspectionTemplateService {
|
||||
|
||||
/**
|
||||
* 创建巡检模板
|
||||
*
|
||||
* @param createReqVO 创建请求
|
||||
* @return 模板ID
|
||||
*/
|
||||
Long createTemplate(InspectionTemplateSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新巡检模板
|
||||
*
|
||||
* @param updateReqVO 更新请求
|
||||
*/
|
||||
void updateTemplate(InspectionTemplateSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除巡检模板
|
||||
*
|
||||
* @param id 模板ID
|
||||
*/
|
||||
void deleteTemplate(Long id);
|
||||
|
||||
/**
|
||||
* 获得巡检模板
|
||||
*
|
||||
* @param id 模板ID
|
||||
* @return 巡检模板
|
||||
*/
|
||||
OpsInspectionTemplateDO getTemplate(Long id);
|
||||
|
||||
/**
|
||||
* 获得巡检模板分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 巡检模板分页
|
||||
*/
|
||||
PageResult<OpsInspectionTemplateDO> getTemplatePage(InspectionTemplatePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 按功能类型获取启用的检查项列表
|
||||
*
|
||||
* @param functionType 功能类型
|
||||
* @return 检查项列表(按 sortOrder 排序)
|
||||
*/
|
||||
List<OpsInspectionTemplateDO> getTemplateListByFunctionType(String functionType);
|
||||
|
||||
/**
|
||||
* 按区域ID获取动态巡检表单(检查项列表)
|
||||
* <p>
|
||||
* 流程: area_id → 查 ops_bus_area 获取 function_type → 查 ops_inspection_template 加载检查项
|
||||
*
|
||||
* @param areaId 区域ID
|
||||
* @return 检查项列表(按 sortOrder 排序)
|
||||
*/
|
||||
List<OpsInspectionTemplateDO> getTemplateListByAreaId(Long areaId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
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.dal.dataobject.area.OpsBusAreaDO;
|
||||
import com.viewsh.module.ops.dal.mysql.area.OpsBusAreaMapper;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionTemplateDO;
|
||||
import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionTemplateMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
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 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_TEMPLATE_NOT_FOUND;
|
||||
|
||||
/**
|
||||
* 巡检检查项模板 Service 实现
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
public class InspectionTemplateServiceImpl implements InspectionTemplateService {
|
||||
|
||||
@Resource
|
||||
private OpsInspectionTemplateMapper inspectionTemplateMapper;
|
||||
|
||||
@Resource
|
||||
private OpsBusAreaMapper opsBusAreaMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createTemplate(InspectionTemplateSaveReqVO createReqVO) {
|
||||
OpsInspectionTemplateDO template = BeanUtils.toBean(createReqVO, OpsInspectionTemplateDO.class);
|
||||
if (template.getIsActive() == null) {
|
||||
template.setIsActive(true);
|
||||
}
|
||||
inspectionTemplateMapper.insert(template);
|
||||
return template.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateTemplate(InspectionTemplateSaveReqVO updateReqVO) {
|
||||
if (updateReqVO.getId() == null) {
|
||||
throw exception(INSPECTION_TEMPLATE_NOT_FOUND);
|
||||
}
|
||||
validateTemplateExists(updateReqVO.getId());
|
||||
OpsInspectionTemplateDO updateObj = BeanUtils.toBean(updateReqVO, OpsInspectionTemplateDO.class);
|
||||
inspectionTemplateMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteTemplate(Long id) {
|
||||
validateTemplateExists(id);
|
||||
inspectionTemplateMapper.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpsInspectionTemplateDO getTemplate(Long id) {
|
||||
return inspectionTemplateMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OpsInspectionTemplateDO> getTemplatePage(InspectionTemplatePageReqVO pageReqVO) {
|
||||
return inspectionTemplateMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<OpsInspectionTemplateDO>()
|
||||
.eqIfPresent(OpsInspectionTemplateDO::getFunctionType, pageReqVO.getFunctionType())
|
||||
.eqIfPresent(OpsInspectionTemplateDO::getIsActive, pageReqVO.getIsActive())
|
||||
.likeIfPresent(OpsInspectionTemplateDO::getItemTitle, pageReqVO.getItemTitle())
|
||||
.orderByAsc(OpsInspectionTemplateDO::getSortOrder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OpsInspectionTemplateDO> getTemplateListByFunctionType(String functionType) {
|
||||
return inspectionTemplateMapper.selectListByFunctionType(functionType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OpsInspectionTemplateDO> getTemplateListByAreaId(Long areaId) {
|
||||
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();
|
||||
}
|
||||
return inspectionTemplateMapper.selectListByFunctionType(functionType);
|
||||
}
|
||||
|
||||
private void validateTemplateExists(Long id) {
|
||||
if (inspectionTemplateMapper.selectById(id) == null) {
|
||||
throw exception(INSPECTION_TEMPLATE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,4 +29,8 @@ 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, "巡检记录不存在");
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
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;
|
||||
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;
|
||||
|
||||
@Resource
|
||||
private InspectionRecordService inspectionRecordService;
|
||||
|
||||
@PostMapping("/verify-location")
|
||||
@Operation(summary = "蓝牙位置校验")
|
||||
@Parameter(name = "areaId", description = "区域ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection:create')")
|
||||
public CommonResult<LocationVerifyResultVO> verifyLocation(
|
||||
@RequestParam("areaId") Long areaId,
|
||||
@Valid @RequestBody List<DetectedBeaconVO> detectedBeacons) {
|
||||
return success(inspectionLocationService.verifyLocation(areaId, detectedBeacons));
|
||||
}
|
||||
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交巡检结果")
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection:create')")
|
||||
public CommonResult<Long> submitInspection(@Valid @RequestBody InspectionSubmitReqVO submitReqVO) {
|
||||
Long inspectorId = SecurityFrameworkUtils.getLoginUserId();
|
||||
return success(inspectionRecordService.submitInspection(submitReqVO, inspectorId));
|
||||
}
|
||||
|
||||
@GetMapping("/record/page")
|
||||
@Operation(summary = "获得巡检记录分页")
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection:query')")
|
||||
public CommonResult<PageResult<InspectionRecordRespVO>> getRecordPage(@Valid InspectionRecordPageReqVO pageReqVO) {
|
||||
PageResult<OpsInspectionRecordDO> pageResult = inspectionRecordService.getRecordPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, InspectionRecordRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/record/stats")
|
||||
@Operation(summary = "获得巡检统计(合格率、不合格热点区域)")
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection:query')")
|
||||
public CommonResult<InspectionStatsRespVO> getInspectionStats(@Valid InspectionStatsReqVO reqVO) {
|
||||
return success(inspectionRecordService.getInspectionStats(reqVO));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
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.InspectionTemplatePageReqVO;
|
||||
import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplateRespVO;
|
||||
import com.viewsh.module.ops.environment.controller.admin.inspection.vo.InspectionTemplateSaveReqVO;
|
||||
import com.viewsh.module.ops.environment.dal.dataobject.inspection.OpsInspectionTemplateDO;
|
||||
import com.viewsh.module.ops.environment.service.inspection.InspectionTemplateService;
|
||||
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/template")
|
||||
@Validated
|
||||
public class InspectionTemplateController {
|
||||
|
||||
@Resource
|
||||
private InspectionTemplateService inspectionTemplateService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建巡检模板")
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection-template:create')")
|
||||
public CommonResult<Long> createTemplate(@Valid @RequestBody InspectionTemplateSaveReqVO createReqVO) {
|
||||
return success(inspectionTemplateService.createTemplate(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新巡检模板")
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection-template:update')")
|
||||
public CommonResult<Boolean> updateTemplate(@Valid @RequestBody InspectionTemplateSaveReqVO updateReqVO) {
|
||||
inspectionTemplateService.updateTemplate(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除巡检模板")
|
||||
@Parameter(name = "id", description = "模板ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection-template:delete')")
|
||||
public CommonResult<Boolean> deleteTemplate(@RequestParam("id") Long id) {
|
||||
inspectionTemplateService.deleteTemplate(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得巡检模板")
|
||||
@Parameter(name = "id", description = "模板ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection-template:query')")
|
||||
public CommonResult<InspectionTemplateRespVO> getTemplate(@RequestParam("id") Long id) {
|
||||
OpsInspectionTemplateDO template = inspectionTemplateService.getTemplate(id);
|
||||
return success(BeanUtils.toBean(template, InspectionTemplateRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得巡检模板分页")
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection-template:query')")
|
||||
public CommonResult<PageResult<InspectionTemplateRespVO>> getTemplatePage(@Valid InspectionTemplatePageReqVO pageReqVO) {
|
||||
PageResult<OpsInspectionTemplateDO> pageResult = inspectionTemplateService.getTemplatePage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, InspectionTemplateRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-area")
|
||||
@Operation(summary = "按区域获取巡检检查项列表(动态表单)")
|
||||
@Parameter(name = "areaId", description = "区域ID", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('ops:inspection-template:query')")
|
||||
public CommonResult<List<InspectionTemplateRespVO>> getTemplateListByArea(@RequestParam("areaId") Long areaId) {
|
||||
List<OpsInspectionTemplateDO> list = inspectionTemplateService.getTemplateListByAreaId(areaId);
|
||||
return success(BeanUtils.toBean(list, InspectionTemplateRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user