1931 lines
54 KiB
Markdown
1931 lines
54 KiB
Markdown
# AR智能巡检管理系统 - 技术设计文档
|
||
|
||
## 一、项目概述
|
||
|
||
### 1.1 项目简介
|
||
|
||
AR智能巡检管理系统是一个基于 RuoYi-Vue-Plus 5.5.1 框架开发的电力设施智能巡检管理平台,通过AR设备实现智能化巡检作业流程管理。
|
||
|
||
### 1.2 技术栈
|
||
|
||
**后端技术栈:**
|
||
- Spring Boot 3.5.7
|
||
- JDK 17/21
|
||
- MyBatis-Plus 3.5.x
|
||
- Sa-Token 1.38.0(权限认证)
|
||
- MySQL 5.7+
|
||
- Redisson(分布式缓存)
|
||
- MapStruct-Plus(对象映射)
|
||
|
||
**前端技术栈:**
|
||
- Vue 3
|
||
- TypeScript
|
||
- Element Plus
|
||
- Vite
|
||
|
||
### 1.3 核心特性
|
||
|
||
- ✅ **无多租户架构** - 简化数据模型,适用于单租户场景
|
||
- ✅ **树形步骤管理** - 支持任意深度的步骤层级结构
|
||
- ✅ **JSON灵活存储** - 区域数据、点位坐标、AI配置使用JSON格式
|
||
- ✅ **执行状态追踪** - 完整的任务执行生命周期管理
|
||
- ✅ **媒体文件管理** - 支持图片、视频、音频等多媒体文件
|
||
|
||
---
|
||
|
||
## 二、需求分析
|
||
|
||
### 2.1 功能模块
|
||
|
||
| 模块名称 | 功能描述 | 核心实体 |
|
||
|---------|---------|---------|
|
||
| AR设备管理 | 管理AR巡检设备基础信息 | ArDevice |
|
||
| 区域管理 | 管理巡检区域及区域数据 | ArRegion |
|
||
| 点位管理 | 管理巡检点位及位置坐标 | ArPoint |
|
||
| 任务模板管理 | 管理巡检任务模板 | ArTask |
|
||
| 步骤管理 | 管理任务步骤树形结构 | ArStep |
|
||
| 任务执行管理 | 管理任务执行记录及状态 | ArExecution |
|
||
| 步骤记录管理 | 管理步骤执行详细记录 | ArStepRecord |
|
||
| 媒体文件管理 | 管理步骤执行过程中的媒体文件 | ArStepMedia |
|
||
|
||
### 2.2 关键需求
|
||
|
||
#### 2.2.1 设计决策
|
||
|
||
**问题1:步骤层级结构**
|
||
- **决策**:支持任意深度的树形结构
|
||
- **实现**:使用 parent_id + ancestors 字段实现
|
||
|
||
**问题2:坐标数据存储**
|
||
- **决策**:使用JSON字符串存储
|
||
- **实现**:使用 JacksonTypeHandler 实现自动序列化/反序列化
|
||
|
||
**问题3:语音/AI字段性质**
|
||
- **决策**:作为步骤配置项(非执行记录)
|
||
- **实现**:字段设计在 ar_step 表中
|
||
|
||
**问题4:执行记录追踪**
|
||
- **决策**:追踪状态、执行数据、AI识别结果、语音文本
|
||
- **实现**:在 ar_step_record 表中设计对应字段
|
||
|
||
#### 2.2.2 数据库调整需求
|
||
|
||
根据实际需求,数据库设计进行了以下调整:
|
||
|
||
| 表名 | 调整内容 |
|
||
|------|---------|
|
||
| ar_device | 新增 device_model 字段 |
|
||
| ar_region | 新增 region_data (JSON) 和 remark 字段 |
|
||
| ar_point | 字段 position 改名为 position_data (JSON),移除 direction,新增 remark |
|
||
| ar_task | 新增 remark 字段 |
|
||
| ar_step | 移除UI预设字段(从点位获取),新增 ai_data (JSON) |
|
||
| ar_step_media | 移除 oss_id 字段 |
|
||
|
||
#### 2.2.3 多租户处理
|
||
|
||
**明确要求:不考虑多租户**
|
||
|
||
实施措施:
|
||
1. 所有数据表均不包含 tenant_id 字段
|
||
2. 所有实体类继承 BaseEntity(非 TenantEntity)
|
||
3. 配置文件设置 `tenant.enable: false`
|
||
4. 所有业务表加入 `tenant.excludes` 列表
|
||
|
||
---
|
||
|
||
## 三、数据库设计
|
||
|
||
### 3.1 数据库表清单
|
||
|
||
系统共包含 8 张业务表,所有表均使用雪花ID作为主键,支持逻辑删除。
|
||
|
||
### 3.2 表结构设计
|
||
|
||
#### 3.2.1 AR设备表 (ar_device)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_device` (
|
||
`id` bigint NOT NULL COMMENT '设备ID',
|
||
`device_name` varchar(100) NOT NULL COMMENT '设备名称',
|
||
`device_no` varchar(50) NOT NULL COMMENT '设备编号',
|
||
`device_model` varchar(100) DEFAULT NULL COMMENT '设备型号',
|
||
`status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
|
||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_device_no` (`device_no`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AR设备表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `device_no`: 设备编号,全局唯一
|
||
- `device_model`: 设备型号(新增需求)
|
||
- `status`: 设备状态(0正常 1停用)
|
||
|
||
#### 3.2.2 巡检区域表 (ar_region)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_region` (
|
||
`id` bigint NOT NULL COMMENT '区域ID',
|
||
`region_name` varchar(100) NOT NULL COMMENT '区域名称',
|
||
`region_code` varchar(50) NOT NULL COMMENT '区域代码',
|
||
`region_data` json DEFAULT NULL COMMENT '区域数据(JSON格式)',
|
||
`status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
|
||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_region_code` (`region_code`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='巡检区域表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `region_code`: 区域代码,全局唯一
|
||
- `region_data`: 区域数据,JSON格式存储(新增需求)
|
||
- `remark`: 备注信息(新增需求)
|
||
|
||
#### 3.2.3 巡检点位表 (ar_point)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_point` (
|
||
`id` bigint NOT NULL COMMENT '点位ID',
|
||
`region_id` bigint NOT NULL COMMENT '所属区域ID',
|
||
`point_name` varchar(100) NOT NULL COMMENT '点位名称',
|
||
`point_code` varchar(50) NOT NULL COMMENT '点位代码',
|
||
`position_data` json DEFAULT NULL COMMENT '位置数据(JSON格式,包含坐标等)',
|
||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_region_point` (`region_id`, `point_code`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='巡检点位表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `point_code`: 点位代码,同一区域内唯一
|
||
- `position_data`: 位置数据,JSON格式(字段改名需求)
|
||
- 移除了 `direction` 字段(按需求调整)
|
||
- `remark`: 备注信息(新增需求)
|
||
|
||
#### 3.2.4 巡检任务模板表 (ar_task)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_task` (
|
||
`id` bigint NOT NULL COMMENT '任务ID',
|
||
`task_name` varchar(100) NOT NULL COMMENT '任务名称',
|
||
`task_code` varchar(50) NOT NULL COMMENT '任务代码',
|
||
`region_id` bigint NOT NULL COMMENT '关联区域ID',
|
||
`task_type` varchar(50) DEFAULT NULL COMMENT '任务类型',
|
||
`status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
|
||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_task_code` (`task_code`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='巡检任务模板表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `task_code`: 任务代码,全局唯一
|
||
- `region_id`: 关联区域ID
|
||
- `remark`: 备注信息(新增需求)
|
||
|
||
#### 3.2.5 巡检步骤表 (ar_step)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_step` (
|
||
`id` bigint NOT NULL COMMENT '步骤ID',
|
||
`task_id` bigint NOT NULL COMMENT '所属任务ID',
|
||
`parent_id` bigint DEFAULT '0' COMMENT '父步骤ID(0表示根节点)',
|
||
`ancestors` varchar(500) DEFAULT '0' COMMENT '祖级列表',
|
||
`step_name` varchar(100) NOT NULL COMMENT '步骤名称',
|
||
`step_content` text COMMENT '步骤内容',
|
||
`content_voice` varchar(255) DEFAULT NULL COMMENT '内容语音URL',
|
||
`order_num` int DEFAULT '0' COMMENT '显示顺序',
|
||
`point_id` bigint DEFAULT NULL COMMENT '关联点位ID',
|
||
|
||
-- 语音交互配置
|
||
`need_voice_read` char(1) DEFAULT '0' COMMENT '是否需要语音播报(0否 1是)',
|
||
`need_voice_rephrase` char(1) DEFAULT '0' COMMENT '是否需要复述(0否 1是)',
|
||
`rephrase_content` text COMMENT '复述内容',
|
||
`rephrase_voice` varchar(255) DEFAULT NULL COMMENT '复述语音URL',
|
||
`need_voice_confirm` char(1) DEFAULT '0' COMMENT '是否需要语音确认(0否 1是)',
|
||
`confirm_content` text COMMENT '确认内容',
|
||
`confirm_voice` varchar(255) DEFAULT NULL COMMENT '确认语音URL',
|
||
`confirm_word` varchar(100) DEFAULT NULL COMMENT '确认关键词',
|
||
|
||
-- AI识别配置
|
||
`need_ai` char(1) DEFAULT '0' COMMENT '是否需要AI识别(0否 1是)',
|
||
`ai_target_name` varchar(100) DEFAULT NULL COMMENT 'AI识别目标名称',
|
||
`ai_data` json DEFAULT NULL COMMENT 'AI配置数据(JSON格式)',
|
||
|
||
`is_operation` char(1) DEFAULT '0' COMMENT '是否需要操作(0否 1是)',
|
||
`is_leaf` char(1) DEFAULT '1' COMMENT '是否叶子节点(0否 1是)',
|
||
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='巡检步骤表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `parent_id` + `ancestors`: 实现树形结构
|
||
- 支持任意深度的步骤层级
|
||
- 语音交互配置字段:复述、确认等
|
||
- `ai_data`: AI配置数据,JSON格式(新增需求)
|
||
- **移除了UI预设字段**(birth_point, birth_direction, prefab 等,按需求调整)
|
||
|
||
#### 3.2.6 任务执行记录表 (ar_execution)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_execution` (
|
||
`id` bigint NOT NULL COMMENT '执行ID',
|
||
`task_id` bigint NOT NULL COMMENT '任务模板ID',
|
||
`execution_code` varchar(50) NOT NULL COMMENT '执行编号',
|
||
`region_id` bigint NOT NULL COMMENT '区域ID',
|
||
`device_id` bigint DEFAULT NULL COMMENT '使用的AR设备ID',
|
||
|
||
-- 执行角色
|
||
`operator_id` bigint DEFAULT NULL COMMENT '操作人ID',
|
||
`operator_name` varchar(50) DEFAULT NULL COMMENT '操作人姓名',
|
||
`custodian_id` bigint DEFAULT NULL COMMENT '监护人ID',
|
||
`custodian_name` varchar(50) DEFAULT NULL COMMENT '监护人姓名',
|
||
`sender_id` bigint DEFAULT NULL COMMENT '送电人ID',
|
||
`sender_name` varchar(50) DEFAULT NULL COMMENT '送电人姓名',
|
||
`recipient_id` bigint DEFAULT NULL COMMENT '受电人ID',
|
||
`recipient_name` varchar(50) DEFAULT NULL COMMENT '受电人姓名',
|
||
`commander_id` bigint DEFAULT NULL COMMENT '指挥人ID',
|
||
`commander_name` varchar(50) DEFAULT NULL COMMENT '指挥人姓名',
|
||
|
||
`status` varchar(20) DEFAULT 'pending' COMMENT '执行状态(pending待执行 in_progress执行中 completed已完成 cancelled已取消)',
|
||
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
|
||
`end_time` datetime DEFAULT NULL COMMENT '结束时间',
|
||
`total_steps` int DEFAULT '0' COMMENT '总步骤数',
|
||
`completed_steps` int DEFAULT '0' COMMENT '已完成步骤数',
|
||
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`),
|
||
UNIQUE KEY `uk_execution_code` (`execution_code`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务执行记录表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `execution_code`: 执行编号,自动生成(格式:EXE-{timestamp})
|
||
- 5种执行角色:操作人、监护人、送电人、受电人、指挥人
|
||
- `status`: 执行状态(pending/in_progress/completed/cancelled)
|
||
- 自动设置 `start_time` 和 `end_time`
|
||
|
||
#### 3.2.7 步骤执行记录表 (ar_step_record)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_step_record` (
|
||
`id` bigint NOT NULL COMMENT '记录ID',
|
||
`execution_id` bigint NOT NULL COMMENT '任务执行ID',
|
||
`step_id` bigint NOT NULL COMMENT '步骤ID',
|
||
`status` varchar(20) DEFAULT 'pending' COMMENT '状态(pending待执行 completed已完成 skipped已跳过)',
|
||
`is_done` char(1) DEFAULT '0' COMMENT '是否完成(0否 1是)',
|
||
`start_time` datetime DEFAULT NULL COMMENT '开始时间',
|
||
`completion_time` datetime DEFAULT NULL COMMENT '完成时间',
|
||
`duration` int DEFAULT NULL COMMENT '耗时(秒)',
|
||
`text_feedback` text COMMENT '文本反馈',
|
||
`voice_text` text COMMENT '语音识别文本',
|
||
`ai_result` json DEFAULT NULL COMMENT 'AI识别结果(JSON格式)',
|
||
`executor_id` bigint DEFAULT NULL COMMENT '执行人ID',
|
||
`executor_name` varchar(50) DEFAULT NULL COMMENT '执行人姓名',
|
||
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='步骤执行记录表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `status`: 步骤执行状态(pending/completed/skipped)
|
||
- `duration`: 耗时,自动计算(完成时间 - 开始时间)
|
||
- `text_feedback`: 文本反馈
|
||
- `voice_text`: 语音识别文本
|
||
- `ai_result`: AI识别结果,JSON格式
|
||
|
||
#### 3.2.8 步骤媒体文件表 (ar_step_media)
|
||
|
||
```sql
|
||
CREATE TABLE `ar_step_media` (
|
||
`id` bigint NOT NULL COMMENT '媒体ID',
|
||
`step_record_id` bigint NOT NULL COMMENT '步骤记录ID',
|
||
`media_type` varchar(20) NOT NULL COMMENT '媒体类型(image图片 video视频 audio音频)',
|
||
`file_url` varchar(500) NOT NULL COMMENT '文件URL',
|
||
`file_name` varchar(200) NOT NULL COMMENT '文件名称',
|
||
`file_size` bigint DEFAULT NULL COMMENT '文件大小(字节)',
|
||
`upload_time` datetime DEFAULT NULL COMMENT '上传时间',
|
||
|
||
`create_dept` bigint DEFAULT NULL COMMENT '创建部门',
|
||
`create_by` bigint DEFAULT NULL COMMENT '创建者',
|
||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||
`update_by` bigint DEFAULT NULL COMMENT '更新者',
|
||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||
`del_flag` tinyint DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
|
||
PRIMARY KEY (`id`)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='步骤媒体文件表';
|
||
```
|
||
|
||
**核心字段说明:**
|
||
- `media_type`: 媒体类型(image/video/audio)
|
||
- **移除了 oss_id 字段**(按需求调整)
|
||
- `upload_time`: 上传时间,自动设置
|
||
|
||
### 3.3 表关系说明
|
||
|
||
```
|
||
ar_region (区域)
|
||
↓ 1:N
|
||
ar_point (点位)
|
||
|
||
ar_region (区域)
|
||
↓ 1:N
|
||
ar_task (任务模板)
|
||
↓ 1:N
|
||
ar_step (步骤) - 树形结构
|
||
|
||
ar_task (任务模板)
|
||
↓ 1:N
|
||
ar_execution (任务执行)
|
||
↓ 1:N
|
||
ar_step_record (步骤记录)
|
||
↓ 1:N
|
||
ar_step_media (媒体文件)
|
||
|
||
ar_device (设备)
|
||
↓ 1:N
|
||
ar_execution (任务执行)
|
||
```
|
||
|
||
---
|
||
|
||
## 四、技术架构设计
|
||
|
||
### 4.1 分层架构
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ Controller Layer │ REST API控制层
|
||
│ (ArDeviceController, etc.) │
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────┐
|
||
│ Service Layer │ 业务逻辑层
|
||
│ (IArDeviceService, Impl, etc.) │
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────┐
|
||
│ Mapper Layer │ 数据访问层
|
||
│ (ArDeviceMapper, etc.) │
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────┐
|
||
│ Database Layer │ MySQL数据库
|
||
│ (ar_device, etc.) │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.2 对象转换流程
|
||
|
||
```
|
||
Client Request (JSON)
|
||
↓
|
||
Controller
|
||
↓ @RequestBody
|
||
Bo (Business Object)
|
||
↓ MapStructUtils.convert()
|
||
Entity
|
||
↓ MyBatis-Plus
|
||
Database
|
||
↓ MyBatis-Plus
|
||
Vo (View Object)
|
||
↓ JSON Response
|
||
Client
|
||
```
|
||
|
||
### 4.3 核心技术组件
|
||
|
||
#### 4.3.1 MyBatis-Plus 配置
|
||
|
||
**主键策略:** 雪花ID(ASSIGN_ID)
|
||
|
||
```java
|
||
@TableId(value = "id")
|
||
private Long id;
|
||
```
|
||
|
||
**逻辑删除:**
|
||
|
||
```java
|
||
@TableLogic
|
||
private Integer delFlag;
|
||
```
|
||
|
||
**JSON字段处理:**
|
||
|
||
```java
|
||
@TableName(value = "ar_region", autoResultMap = true)
|
||
public class ArRegion extends BaseEntity {
|
||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||
private Map<String, Object> regionData;
|
||
}
|
||
```
|
||
|
||
#### 4.3.2 MapStruct-Plus 对象映射
|
||
|
||
**实体与Bo互转:**
|
||
|
||
```java
|
||
@Data
|
||
@AutoMapper(target = ArDevice.class, reverseConvertGenerate = false)
|
||
public class ArDeviceBo extends BaseEntity {
|
||
// fields...
|
||
}
|
||
```
|
||
|
||
**实体转Vo:**
|
||
|
||
```java
|
||
@Data
|
||
@AutoMapper(target = ArDevice.class)
|
||
public class ArDeviceVo implements Serializable {
|
||
// fields...
|
||
}
|
||
```
|
||
|
||
**使用方式:**
|
||
|
||
```java
|
||
ArDevice entity = MapstructUtils.convert(bo, ArDevice.class);
|
||
```
|
||
|
||
#### 4.3.3 Sa-Token 权限控制
|
||
|
||
**权限注解:**
|
||
|
||
```java
|
||
@SaCheckPermission("inspection:device:list")
|
||
@GetMapping("/list")
|
||
public TableDataInfo<ArDeviceVo> list(ArDeviceBo bo, PageQuery pageQuery) {
|
||
return arDeviceService.queryPageList(bo, pageQuery);
|
||
}
|
||
```
|
||
|
||
**权限标识格式:** `模块:功能:操作`
|
||
|
||
#### 4.3.4 数据验证
|
||
|
||
**分组验证:**
|
||
|
||
```java
|
||
// 新增时验证
|
||
@NotBlank(message = "设备名称不能为空", groups = {AddGroup.class})
|
||
private String deviceName;
|
||
|
||
// 编辑时验证
|
||
@NotNull(message = "设备ID不能为空", groups = {EditGroup.class})
|
||
private Long id;
|
||
```
|
||
|
||
---
|
||
|
||
## 五、模块设计
|
||
|
||
### 5.1 模块结构
|
||
|
||
```
|
||
ruoyi-modules/
|
||
└── ruoyi-inspection/
|
||
├── pom.xml
|
||
└── src/main/java/org/dromara/inspection/
|
||
├── controller/ # 控制器层
|
||
│ ├── ArDeviceController.java
|
||
│ ├── ArRegionController.java
|
||
│ ├── ArPointController.java
|
||
│ ├── ArTaskController.java
|
||
│ ├── ArStepController.java
|
||
│ ├── ArExecutionController.java
|
||
│ ├── ArStepRecordController.java
|
||
│ └── ArStepMediaController.java
|
||
├── service/ # 服务接口
|
||
│ ├── IArDeviceService.java
|
||
│ └── impl/ # 服务实现
|
||
│ └── ArDeviceServiceImpl.java
|
||
├── mapper/ # Mapper接口
|
||
│ └── ArDeviceMapper.java
|
||
└── domain/ # 领域对象
|
||
├── ArDevice.java # 实体
|
||
├── bo/ # 业务对象
|
||
│ └── ArDeviceBo.java
|
||
└── vo/ # 视图对象
|
||
└── ArDeviceVo.java
|
||
```
|
||
|
||
### 5.2 标准CRUD实现模式
|
||
|
||
#### 5.2.1 Controller层标准实现
|
||
|
||
```java
|
||
@Validated
|
||
@RequiredArgsConstructor
|
||
@RestController
|
||
@RequestMapping("/inspection/device")
|
||
public class ArDeviceController extends BaseController {
|
||
|
||
private final IArDeviceService arDeviceService;
|
||
|
||
// 1. 分页查询列表
|
||
@SaCheckPermission("inspection:device:list")
|
||
@GetMapping("/list")
|
||
public TableDataInfo<ArDeviceVo> list(
|
||
@Validated(QueryGroup.class) ArDeviceBo bo,
|
||
PageQuery pageQuery) {
|
||
return arDeviceService.queryPageList(bo, pageQuery);
|
||
}
|
||
|
||
// 2. 导出Excel
|
||
@SaCheckPermission("inspection:device:export")
|
||
@Log(title = "AR设备", businessType = BusinessType.EXPORT)
|
||
@PostMapping("/export")
|
||
public void export(ArDeviceBo bo, HttpServletResponse response) {
|
||
List<ArDeviceVo> list = arDeviceService.queryList(bo);
|
||
ExcelUtil.exportExcel(list, "AR设备", ArDeviceVo.class, response);
|
||
}
|
||
|
||
// 3. 查询详情
|
||
@SaCheckPermission("inspection:device:query")
|
||
@GetMapping("/{id}")
|
||
public R<ArDeviceVo> getInfo(@PathVariable("id") Long id) {
|
||
return R.ok(arDeviceService.queryById(id));
|
||
}
|
||
|
||
// 4. 新增
|
||
@SaCheckPermission("inspection:device:add")
|
||
@Log(title = "AR设备", businessType = BusinessType.INSERT)
|
||
@RepeatSubmit
|
||
@PostMapping()
|
||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ArDeviceBo bo) {
|
||
return toAjax(arDeviceService.insertByBo(bo));
|
||
}
|
||
|
||
// 5. 修改
|
||
@SaCheckPermission("inspection:device:edit")
|
||
@Log(title = "AR设备", businessType = BusinessType.UPDATE)
|
||
@RepeatSubmit
|
||
@PutMapping()
|
||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ArDeviceBo bo) {
|
||
return toAjax(arDeviceService.updateByBo(bo));
|
||
}
|
||
|
||
// 6. 删除
|
||
@SaCheckPermission("inspection:device:remove")
|
||
@Log(title = "AR设备", businessType = BusinessType.DELETE)
|
||
@DeleteMapping("/{ids}")
|
||
public R<Void> remove(@PathVariable Long[] ids) {
|
||
return toAjax(arDeviceService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 5.2.2 Service层标准实现
|
||
|
||
```java
|
||
@RequiredArgsConstructor
|
||
@Service
|
||
public class ArDeviceServiceImpl implements IArDeviceService {
|
||
|
||
private final ArDeviceMapper baseMapper;
|
||
|
||
@Override
|
||
public ArDeviceVo queryById(Long id) {
|
||
return baseMapper.selectVoById(id);
|
||
}
|
||
|
||
@Override
|
||
public TableDataInfo<ArDeviceVo> queryPageList(ArDeviceBo bo, PageQuery pageQuery) {
|
||
LambdaQueryWrapper<ArDevice> lqw = buildQueryWrapper(bo);
|
||
Page<ArDeviceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||
return TableDataInfo.build(result);
|
||
}
|
||
|
||
@Override
|
||
public List<ArDeviceVo> queryList(ArDeviceBo bo) {
|
||
return baseMapper.selectVoList(buildQueryWrapper(bo));
|
||
}
|
||
|
||
private LambdaQueryWrapper<ArDevice> buildQueryWrapper(ArDeviceBo bo) {
|
||
LambdaQueryWrapper<ArDevice> lqw = Wrappers.lambdaQuery();
|
||
lqw.like(StringUtils.isNotBlank(bo.getDeviceName()),
|
||
ArDevice::getDeviceName, bo.getDeviceName());
|
||
lqw.eq(StringUtils.isNotBlank(bo.getDeviceNo()),
|
||
ArDevice::getDeviceNo, bo.getDeviceNo());
|
||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()),
|
||
ArDevice::getStatus, bo.getStatus());
|
||
return lqw;
|
||
}
|
||
|
||
@Override
|
||
public Boolean insertByBo(ArDeviceBo bo) {
|
||
ArDevice add = MapstructUtils.convert(bo, ArDevice.class);
|
||
validEntityBeforeSave(add);
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
if (flag) {
|
||
bo.setId(add.getId());
|
||
}
|
||
return flag;
|
||
}
|
||
|
||
@Override
|
||
public Boolean updateByBo(ArDeviceBo bo) {
|
||
ArDevice update = MapstructUtils.convert(bo, ArDevice.class);
|
||
validEntityBeforeSave(update);
|
||
return baseMapper.updateById(update) > 0;
|
||
}
|
||
|
||
private void validEntityBeforeSave(ArDevice entity) {
|
||
// 唯一性校验:设备编号
|
||
if (StringUtils.isNotBlank(entity.getDeviceNo())) {
|
||
LambdaQueryWrapper<ArDevice> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArDevice::getDeviceNo, entity.getDeviceNo());
|
||
lqw.ne(entity.getId() != null, ArDevice::getId, entity.getId());
|
||
long count = baseMapper.selectCount(lqw);
|
||
if (count > 0) {
|
||
throw new ServiceException("设备编号已存在!");
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||
if (isValid) {
|
||
List<ArDevice> list = baseMapper.selectByIds(ids);
|
||
if (list.size() != ids.size()) {
|
||
throw new ServiceException("您没有删除权限!");
|
||
}
|
||
}
|
||
return baseMapper.deleteByIds(ids) > 0;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 特殊模块设计
|
||
|
||
#### 5.3.1 步骤管理(树形结构)
|
||
|
||
**树形VO设计:**
|
||
|
||
```java
|
||
@Data
|
||
public class ArStepTreeVo implements Serializable {
|
||
private Long id;
|
||
private Long taskId;
|
||
private Long parentId;
|
||
private String stepName;
|
||
// ... 其他字段
|
||
|
||
// 核心:子节点列表
|
||
private List<ArStepTreeVo> children;
|
||
}
|
||
```
|
||
|
||
**树形查询接口:**
|
||
|
||
```java
|
||
@GetMapping("/tree/{taskId}")
|
||
public R<List<ArStepTreeVo>> tree(@PathVariable("taskId") Long taskId) {
|
||
return R.ok(arStepService.queryStepTree(taskId));
|
||
}
|
||
```
|
||
|
||
**递归构建树形结构:**
|
||
|
||
```java
|
||
@Override
|
||
public List<ArStepTreeVo> queryStepTree(Long taskId) {
|
||
// 1. 查询所有步骤
|
||
LambdaQueryWrapper<ArStep> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArStep::getTaskId, taskId);
|
||
lqw.orderByAsc(ArStep::getOrderNum);
|
||
List<ArStepVo> allSteps = baseMapper.selectVoList(lqw);
|
||
|
||
// 2. 递归构建树
|
||
return buildStepTree(allSteps, 0L);
|
||
}
|
||
|
||
private List<ArStepTreeVo> buildStepTree(List<ArStepVo> allSteps, Long parentId) {
|
||
List<ArStepTreeVo> tree = new ArrayList<>();
|
||
for (ArStepVo step : allSteps) {
|
||
if (step.getParentId().equals(parentId)) {
|
||
ArStepTreeVo treeNode = new ArStepTreeVo();
|
||
BeanUtils.copyProperties(step, treeNode);
|
||
|
||
// 递归查找子节点
|
||
List<ArStepTreeVo> children = buildStepTree(allSteps, step.getId());
|
||
treeNode.setChildren(children);
|
||
|
||
tree.add(treeNode);
|
||
}
|
||
}
|
||
return tree;
|
||
}
|
||
```
|
||
|
||
**自动维护ancestors字段:**
|
||
|
||
```java
|
||
@Override
|
||
public Boolean insertByBo(ArStepBo bo) {
|
||
ArStep add = MapstructUtils.convert(bo, ArStep.class);
|
||
|
||
// 自动设置ancestors
|
||
if (add.getParentId() != null && add.getParentId() != 0) {
|
||
ArStep parent = baseMapper.selectById(add.getParentId());
|
||
if (parent != null) {
|
||
add.setAncestors(parent.getAncestors() + "," + add.getParentId());
|
||
} else {
|
||
add.setAncestors("0," + add.getParentId());
|
||
}
|
||
} else {
|
||
add.setAncestors("0");
|
||
}
|
||
|
||
// 设置初始状态
|
||
if (StringUtils.isBlank(add.getIsLeaf())) {
|
||
add.setIsLeaf("1");
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
|
||
// 如果插入成功且有父节点,更新父节点的isLeaf
|
||
if (flag && add.getParentId() != null && add.getParentId() != 0) {
|
||
ArStep parent = new ArStep();
|
||
parent.setId(add.getParentId());
|
||
parent.setIsLeaf("0");
|
||
baseMapper.updateById(parent);
|
||
}
|
||
|
||
if (flag) {
|
||
bo.setId(add.getId());
|
||
}
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
**级联删除:**
|
||
|
||
```java
|
||
@Override
|
||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||
if (isValid) {
|
||
List<ArStep> list = baseMapper.selectByIds(ids);
|
||
if (list.size() != ids.size()) {
|
||
throw new ServiceException("您没有删除权限!");
|
||
}
|
||
}
|
||
|
||
// 级联删除子步骤
|
||
for (Long id : ids) {
|
||
LambdaQueryWrapper<ArStep> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArStep::getParentId, id);
|
||
long count = baseMapper.selectCount(lqw);
|
||
if (count > 0) {
|
||
List<ArStep> children = baseMapper.selectList(lqw);
|
||
List<Long> childrenIds = children.stream()
|
||
.map(ArStep::getId)
|
||
.collect(Collectors.toList());
|
||
// 递归删除子节点
|
||
deleteWithValidByIds(childrenIds, false);
|
||
}
|
||
}
|
||
|
||
return baseMapper.deleteByIds(ids) > 0;
|
||
}
|
||
```
|
||
|
||
#### 5.3.2 任务执行管理(状态管理)
|
||
|
||
**自动生成执行编号:**
|
||
|
||
```java
|
||
@Override
|
||
public Boolean insertByBo(ArExecutionBo bo) {
|
||
ArExecution add = MapstructUtils.convert(bo, ArExecution.class);
|
||
|
||
// 自动生成执行编号
|
||
if (StringUtils.isBlank(add.getExecutionCode())) {
|
||
add.setExecutionCode("EXE-" + System.currentTimeMillis());
|
||
}
|
||
|
||
// 设置初始状态
|
||
if (StringUtils.isBlank(add.getStatus())) {
|
||
add.setStatus("pending");
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
if (flag) {
|
||
bo.setId(add.getId());
|
||
}
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
**状态变化自动设置时间戳:**
|
||
|
||
```java
|
||
@Override
|
||
public Boolean updateByBo(ArExecutionBo bo) {
|
||
ArExecution update = MapstructUtils.convert(bo, ArExecution.class);
|
||
|
||
// 状态变为in_progress时,自动设置开始时间
|
||
if ("in_progress".equals(update.getStatus()) && update.getStartTime() == null) {
|
||
update.setStartTime(new Date());
|
||
}
|
||
|
||
// 状态变为completed或cancelled时,自动设置结束时间
|
||
if (("completed".equals(update.getStatus()) || "cancelled".equals(update.getStatus()))
|
||
&& update.getEndTime() == null) {
|
||
update.setEndTime(new Date());
|
||
}
|
||
|
||
return baseMapper.updateById(update) > 0;
|
||
}
|
||
```
|
||
|
||
#### 5.3.3 步骤记录管理(自动计算耗时)
|
||
|
||
```java
|
||
@Override
|
||
public Boolean updateByBo(ArStepRecordBo bo) {
|
||
ArStepRecord update = MapstructUtils.convert(bo, ArStepRecord.class);
|
||
|
||
// 状态变为completed时,自动设置完成时间并计算耗时
|
||
if ("completed".equals(update.getStatus()) && update.getCompletionTime() == null) {
|
||
update.setCompletionTime(new Date());
|
||
update.setIsDone("1");
|
||
|
||
// 自动计算耗时(秒)
|
||
if (update.getStartTime() != null) {
|
||
long duration = (update.getCompletionTime().getTime()
|
||
- update.getStartTime().getTime()) / 1000;
|
||
update.setDuration((int) duration);
|
||
}
|
||
}
|
||
|
||
return baseMapper.updateById(update) > 0;
|
||
}
|
||
```
|
||
|
||
#### 5.3.4 媒体文件管理(自动设置上传时间)
|
||
|
||
```java
|
||
@Override
|
||
public Boolean insertByBo(ArStepMediaBo bo) {
|
||
ArStepMedia add = MapstructUtils.convert(bo, ArStepMedia.class);
|
||
validEntityBeforeSave(add);
|
||
|
||
// 自动设置上传时间
|
||
if (add.getUploadTime() == null) {
|
||
add.setUploadTime(new Date());
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
if (flag) {
|
||
bo.setId(add.getId());
|
||
}
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 六、API接口设计
|
||
|
||
### 6.1 RESTful API规范
|
||
|
||
所有API遵循统一的RESTful风格:
|
||
|
||
| HTTP方法 | 路径 | 功能 | 权限标识 |
|
||
|---------|------|-----|---------|
|
||
| GET | /inspection/{module}/list | 分页查询列表 | {module}:list |
|
||
| POST | /inspection/{module}/export | 导出Excel | {module}:export |
|
||
| GET | /inspection/{module}/{id} | 查询详情 | {module}:query |
|
||
| POST | /inspection/{module} | 新增 | {module}:add |
|
||
| PUT | /inspection/{module} | 修改 | {module}:edit |
|
||
| DELETE | /inspection/{module}/{ids} | 批量删除 | {module}:remove |
|
||
|
||
### 6.2 模块API列表
|
||
|
||
#### 6.2.1 AR设备管理
|
||
|
||
```
|
||
GET /inspection/device/list # 分页查询设备列表
|
||
POST /inspection/device/export # 导出设备列表
|
||
GET /inspection/device/{id} # 查询设备详情
|
||
POST /inspection/device # 新增设备
|
||
PUT /inspection/device # 修改设备
|
||
DELETE /inspection/device/{ids} # 删除设备
|
||
```
|
||
|
||
**查询参数示例:**
|
||
```json
|
||
{
|
||
"deviceName": "设备1",
|
||
"deviceNo": "DEV001",
|
||
"status": "0",
|
||
"pageNum": 1,
|
||
"pageSize": 10
|
||
}
|
||
```
|
||
|
||
#### 6.2.2 区域管理
|
||
|
||
```
|
||
GET /inspection/region/list # 分页查询区域列表
|
||
POST /inspection/region/export # 导出区域列表
|
||
GET /inspection/region/{id} # 查询区域详情
|
||
POST /inspection/region # 新增区域
|
||
PUT /inspection/region # 修改区域
|
||
DELETE /inspection/region/{ids} # 删除区域
|
||
```
|
||
|
||
**新增请求体示例:**
|
||
```json
|
||
{
|
||
"regionName": "A区",
|
||
"regionCode": "REGION-A",
|
||
"regionData": {
|
||
"area": 1000,
|
||
"building": "主楼",
|
||
"floor": 3
|
||
},
|
||
"status": "0",
|
||
"remark": "主要巡检区域"
|
||
}
|
||
```
|
||
|
||
#### 6.2.3 点位管理
|
||
|
||
```
|
||
GET /inspection/point/list # 分页查询点位列表
|
||
POST /inspection/point/export # 导出点位列表
|
||
GET /inspection/point/{id} # 查询点位详情
|
||
POST /inspection/point # 新增点位
|
||
PUT /inspection/point # 修改点位
|
||
DELETE /inspection/point/{ids} # 删除点位
|
||
```
|
||
|
||
**新增请求体示例:**
|
||
```json
|
||
{
|
||
"regionId": 1,
|
||
"pointName": "配电柜A1",
|
||
"pointCode": "POINT-A1",
|
||
"positionData": {
|
||
"x": 10.5,
|
||
"y": 20.3,
|
||
"z": 1.5,
|
||
"rotation": {
|
||
"x": 0,
|
||
"y": 90,
|
||
"z": 0
|
||
}
|
||
},
|
||
"remark": "主配电柜"
|
||
}
|
||
```
|
||
|
||
#### 6.2.4 任务模板管理
|
||
|
||
```
|
||
GET /inspection/task/list # 分页查询任务列表
|
||
POST /inspection/task/export # 导出任务列表
|
||
GET /inspection/task/{id} # 查询任务详情
|
||
POST /inspection/task # 新增任务
|
||
PUT /inspection/task # 修改任务
|
||
DELETE /inspection/task/{ids} # 删除任务
|
||
```
|
||
|
||
#### 6.2.5 步骤管理
|
||
|
||
```
|
||
GET /inspection/step/list # 分页查询步骤列表
|
||
GET /inspection/step/tree/{taskId} # 查询任务的步骤树
|
||
POST /inspection/step/export # 导出步骤列表
|
||
GET /inspection/step/{id} # 查询步骤详情
|
||
POST /inspection/step # 新增步骤
|
||
PUT /inspection/step # 修改步骤
|
||
DELETE /inspection/step/{ids} # 删除步骤(级联)
|
||
```
|
||
|
||
**树形结构返回示例:**
|
||
```json
|
||
[
|
||
{
|
||
"id": 1,
|
||
"taskId": 1,
|
||
"parentId": 0,
|
||
"stepName": "开始巡检",
|
||
"orderNum": 1,
|
||
"children": [
|
||
{
|
||
"id": 2,
|
||
"parentId": 1,
|
||
"stepName": "检查电压",
|
||
"orderNum": 1,
|
||
"children": []
|
||
},
|
||
{
|
||
"id": 3,
|
||
"parentId": 1,
|
||
"stepName": "检查电流",
|
||
"orderNum": 2,
|
||
"children": []
|
||
}
|
||
]
|
||
}
|
||
]
|
||
```
|
||
|
||
**新增步骤请求体示例:**
|
||
```json
|
||
{
|
||
"taskId": 1,
|
||
"parentId": 0,
|
||
"stepName": "检查配电柜",
|
||
"stepContent": "检查配电柜外观及指示灯状态",
|
||
"orderNum": 1,
|
||
"pointId": 1,
|
||
"needVoiceRead": "1",
|
||
"needAi": "1",
|
||
"aiTargetName": "配电柜指示灯",
|
||
"aiData": {
|
||
"modelName": "yolov8",
|
||
"confidence": 0.8,
|
||
"classes": ["红灯", "绿灯", "黄灯"]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 6.2.6 任务执行管理
|
||
|
||
```
|
||
GET /inspection/execution/list # 分页查询执行记录列表
|
||
POST /inspection/execution/export # 导出执行记录
|
||
GET /inspection/execution/{id} # 查询执行记录详情
|
||
POST /inspection/execution # 新增执行记录
|
||
PUT /inspection/execution # 修改执行记录
|
||
DELETE /inspection/execution/{ids} # 删除执行记录
|
||
```
|
||
|
||
**新增执行记录示例:**
|
||
```json
|
||
{
|
||
"taskId": 1,
|
||
"regionId": 1,
|
||
"deviceId": 1,
|
||
"operatorId": 1001,
|
||
"operatorName": "张三",
|
||
"custodianId": 1002,
|
||
"custodianName": "李四",
|
||
"status": "pending"
|
||
}
|
||
```
|
||
|
||
**更新状态示例:**
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"status": "in_progress"
|
||
// 自动设置startTime
|
||
}
|
||
```
|
||
|
||
#### 6.2.7 步骤记录管理
|
||
|
||
```
|
||
GET /inspection/stepRecord/list # 分页查询步骤记录列表
|
||
POST /inspection/stepRecord/export # 导出步骤记录
|
||
GET /inspection/stepRecord/{id} # 查询步骤记录详情
|
||
POST /inspection/stepRecord # 新增步骤记录
|
||
PUT /inspection/stepRecord # 修改步骤记录
|
||
DELETE /inspection/stepRecord/{ids} # 删除步骤记录
|
||
```
|
||
|
||
**新增步骤记录示例:**
|
||
```json
|
||
{
|
||
"executionId": 1,
|
||
"stepId": 1,
|
||
"startTime": "2025-01-13 10:00:00"
|
||
}
|
||
```
|
||
|
||
**完成步骤示例:**
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"status": "completed",
|
||
"textFeedback": "检查完成,设备状态正常",
|
||
"voiceText": "设备状态正常",
|
||
"aiResult": {
|
||
"detected": true,
|
||
"confidence": 0.95,
|
||
"status": "正常"
|
||
}
|
||
// 自动设置completionTime和计算duration
|
||
}
|
||
```
|
||
|
||
#### 6.2.8 媒体文件管理
|
||
|
||
```
|
||
GET /inspection/stepMedia/list # 分页查询媒体文件列表
|
||
POST /inspection/stepMedia/export # 导出媒体文件列表
|
||
GET /inspection/stepMedia/{id} # 查询媒体文件详情
|
||
POST /inspection/stepMedia # 新增媒体文件
|
||
PUT /inspection/stepMedia # 修改媒体文件
|
||
DELETE /inspection/stepMedia/{ids} # 删除媒体文件
|
||
```
|
||
|
||
**新增媒体文件示例:**
|
||
```json
|
||
{
|
||
"stepRecordId": 1,
|
||
"mediaType": "image",
|
||
"fileUrl": "https://oss.example.com/inspection/20250113/abc123.jpg",
|
||
"fileName": "配电柜照片.jpg",
|
||
"fileSize": 2048576
|
||
// 自动设置uploadTime
|
||
}
|
||
```
|
||
|
||
### 6.3 统一响应格式
|
||
|
||
**成功响应:**
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "操作成功",
|
||
"data": {
|
||
// 响应数据
|
||
}
|
||
}
|
||
```
|
||
|
||
**分页响应:**
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"msg": "查询成功",
|
||
"rows": [
|
||
// 数据列表
|
||
],
|
||
"total": 100
|
||
}
|
||
```
|
||
|
||
**失败响应:**
|
||
```json
|
||
{
|
||
"code": 500,
|
||
"msg": "设备编号已存在!"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 七、关键技术实现
|
||
|
||
### 7.1 多租户禁用方案
|
||
|
||
#### 7.1.1 配置文件修改
|
||
|
||
**application.yml:**
|
||
```yaml
|
||
# 多租户配置
|
||
tenant:
|
||
# 是否开启
|
||
enable: false
|
||
# 排除表(不进行多租户处理的表)
|
||
excludes:
|
||
- sys_menu
|
||
- sys_tenant
|
||
- sys_tenant_package
|
||
- ar_device
|
||
- ar_region
|
||
- ar_point
|
||
- ar_task
|
||
- ar_step
|
||
- ar_execution
|
||
- ar_step_record
|
||
- ar_step_media
|
||
```
|
||
|
||
**application-dev.yml, application-prod.yml:**
|
||
同样设置 `tenant.enable: false`
|
||
|
||
#### 7.1.2 实体类设计
|
||
|
||
所有实体类继承 **BaseEntity** 而非 TenantEntity:
|
||
|
||
```java
|
||
@Data
|
||
@EqualsAndHashCode(callSuper = true)
|
||
@TableName("ar_device")
|
||
public class ArDevice extends BaseEntity {
|
||
// 不包含tenantId字段
|
||
}
|
||
```
|
||
|
||
### 7.2 JSON字段处理方案
|
||
|
||
#### 7.2.1 实体类配置
|
||
|
||
```java
|
||
@Data
|
||
@EqualsAndHashCode(callSuper = true)
|
||
@TableName(value = "ar_region", autoResultMap = true) // 必须设置autoResultMap
|
||
public class ArRegion extends BaseEntity {
|
||
|
||
@TableField(typeHandler = JacksonTypeHandler.class) // 指定类型处理器
|
||
private Map<String, Object> regionData;
|
||
}
|
||
```
|
||
|
||
**关键点:**
|
||
1. `@TableName(autoResultMap = true)` - 启用自动结果映射
|
||
2. `@TableField(typeHandler = JacksonTypeHandler.class)` - 使用Jackson处理器
|
||
3. 字段类型使用 `Map<String, Object>` 或自定义POJO
|
||
|
||
#### 7.2.2 数据库字段类型
|
||
|
||
MySQL使用 `json` 类型:
|
||
```sql
|
||
`region_data` json DEFAULT NULL COMMENT '区域数据(JSON格式)'
|
||
```
|
||
|
||
#### 7.2.3 使用示例
|
||
|
||
**新增数据:**
|
||
```java
|
||
ArRegion region = new ArRegion();
|
||
region.setRegionName("A区");
|
||
Map<String, Object> data = new HashMap<>();
|
||
data.put("area", 1000);
|
||
data.put("building", "主楼");
|
||
region.setRegionData(data);
|
||
baseMapper.insert(region);
|
||
```
|
||
|
||
**查询数据:**
|
||
```java
|
||
ArRegion region = baseMapper.selectById(1L);
|
||
Map<String, Object> data = region.getRegionData();
|
||
Integer area = (Integer) data.get("area");
|
||
```
|
||
|
||
### 7.3 树形结构实现方案
|
||
|
||
#### 7.3.1 数据库字段设计
|
||
|
||
```sql
|
||
`parent_id` bigint DEFAULT '0' COMMENT '父步骤ID(0表示根节点)',
|
||
`ancestors` varchar(500) DEFAULT '0' COMMENT '祖级列表',
|
||
`is_leaf` char(1) DEFAULT '1' COMMENT '是否叶子节点(0否 1是)'
|
||
```
|
||
|
||
#### 7.3.2 ancestors字段说明
|
||
|
||
ancestors存储从根节点到父节点的完整路径:
|
||
- 根节点:`"0"`
|
||
- 一级节点:`"0,1"`
|
||
- 二级节点:`"0,1,2"`
|
||
- 三级节点:`"0,1,2,3"`
|
||
|
||
**优势:**
|
||
- 快速查询所有祖先节点
|
||
- 快速查询所有子孙节点
|
||
- 支持任意深度
|
||
|
||
#### 7.3.3 新增节点自动维护ancestors
|
||
|
||
```java
|
||
public Boolean insertByBo(ArStepBo bo) {
|
||
ArStep add = MapstructUtils.convert(bo, ArStep.class);
|
||
|
||
// 自动设置ancestors
|
||
if (add.getParentId() != null && add.getParentId() != 0) {
|
||
ArStep parent = baseMapper.selectById(add.getParentId());
|
||
if (parent != null) {
|
||
// 继承父节点的ancestors + 父节点ID
|
||
add.setAncestors(parent.getAncestors() + "," + add.getParentId());
|
||
} else {
|
||
add.setAncestors("0," + add.getParentId());
|
||
}
|
||
} else {
|
||
add.setAncestors("0");
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
|
||
// 更新父节点的isLeaf标识
|
||
if (flag && add.getParentId() != null && add.getParentId() != 0) {
|
||
ArStep parent = new ArStep();
|
||
parent.setId(add.getParentId());
|
||
parent.setIsLeaf("0"); // 有子节点,不是叶子节点
|
||
baseMapper.updateById(parent);
|
||
}
|
||
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
#### 7.3.4 递归构建树形结构
|
||
|
||
```java
|
||
public List<ArStepTreeVo> queryStepTree(Long taskId) {
|
||
// 1. 一次性查询所有步骤
|
||
LambdaQueryWrapper<ArStep> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArStep::getTaskId, taskId);
|
||
lqw.orderByAsc(ArStep::getOrderNum);
|
||
List<ArStepVo> allSteps = baseMapper.selectVoList(lqw);
|
||
|
||
// 2. 递归构建树(从根节点parentId=0开始)
|
||
return buildStepTree(allSteps, 0L);
|
||
}
|
||
|
||
private List<ArStepTreeVo> buildStepTree(List<ArStepVo> allSteps, Long parentId) {
|
||
List<ArStepTreeVo> tree = new ArrayList<>();
|
||
|
||
for (ArStepVo step : allSteps) {
|
||
if (step.getParentId().equals(parentId)) {
|
||
// 找到当前parentId的子节点
|
||
ArStepTreeVo treeNode = new ArStepTreeVo();
|
||
BeanUtils.copyProperties(step, treeNode);
|
||
|
||
// 递归查找子节点的子节点
|
||
List<ArStepTreeVo> children = buildStepTree(allSteps, step.getId());
|
||
treeNode.setChildren(children);
|
||
|
||
tree.add(treeNode);
|
||
}
|
||
}
|
||
|
||
return tree;
|
||
}
|
||
```
|
||
|
||
#### 7.3.5 级联删除
|
||
|
||
```java
|
||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||
// 权限校验...
|
||
|
||
// 级联删除所有子节点
|
||
for (Long id : ids) {
|
||
LambdaQueryWrapper<ArStep> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArStep::getParentId, id);
|
||
long count = baseMapper.selectCount(lqw);
|
||
|
||
if (count > 0) {
|
||
// 查询所有子节点
|
||
List<ArStep> children = baseMapper.selectList(lqw);
|
||
List<Long> childrenIds = children.stream()
|
||
.map(ArStep::getId)
|
||
.collect(Collectors.toList());
|
||
|
||
// 递归删除子节点
|
||
deleteWithValidByIds(childrenIds, false);
|
||
}
|
||
}
|
||
|
||
return baseMapper.deleteByIds(ids) > 0;
|
||
}
|
||
```
|
||
|
||
### 7.4 自动字段管理方案
|
||
|
||
#### 7.4.1 自动生成执行编号
|
||
|
||
```java
|
||
@Override
|
||
public Boolean insertByBo(ArExecutionBo bo) {
|
||
ArExecution add = MapstructUtils.convert(bo, ArExecution.class);
|
||
|
||
// 自动生成执行编号:EXE-{时间戳}
|
||
if (StringUtils.isBlank(add.getExecutionCode())) {
|
||
add.setExecutionCode("EXE-" + System.currentTimeMillis());
|
||
}
|
||
|
||
// 设置初始状态
|
||
if (StringUtils.isBlank(add.getStatus())) {
|
||
add.setStatus("pending");
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
if (flag) {
|
||
bo.setId(add.getId());
|
||
}
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
#### 7.4.2 状态变化自动设置时间戳
|
||
|
||
```java
|
||
@Override
|
||
public Boolean updateByBo(ArExecutionBo bo) {
|
||
ArExecution update = MapstructUtils.convert(bo, ArExecution.class);
|
||
|
||
// pending -> in_progress:设置开始时间
|
||
if ("in_progress".equals(update.getStatus()) && update.getStartTime() == null) {
|
||
update.setStartTime(new Date());
|
||
}
|
||
|
||
// -> completed/cancelled:设置结束时间
|
||
if (("completed".equals(update.getStatus()) || "cancelled".equals(update.getStatus()))
|
||
&& update.getEndTime() == null) {
|
||
update.setEndTime(new Date());
|
||
}
|
||
|
||
return baseMapper.updateById(update) > 0;
|
||
}
|
||
```
|
||
|
||
#### 7.4.3 自动计算耗时
|
||
|
||
```java
|
||
@Override
|
||
public Boolean updateByBo(ArStepRecordBo bo) {
|
||
ArStepRecord update = MapstructUtils.convert(bo, ArStepRecord.class);
|
||
|
||
// 状态变为completed:自动设置完成时间、isDone标识、计算耗时
|
||
if ("completed".equals(update.getStatus()) && update.getCompletionTime() == null) {
|
||
update.setCompletionTime(new Date());
|
||
update.setIsDone("1");
|
||
|
||
// 计算耗时(秒)
|
||
if (update.getStartTime() != null) {
|
||
long durationMs = update.getCompletionTime().getTime()
|
||
- update.getStartTime().getTime();
|
||
update.setDuration((int) (durationMs / 1000));
|
||
}
|
||
}
|
||
|
||
return baseMapper.updateById(update) > 0;
|
||
}
|
||
```
|
||
|
||
#### 7.4.4 自动设置上传时间
|
||
|
||
```java
|
||
@Override
|
||
public Boolean insertByBo(ArStepMediaBo bo) {
|
||
ArStepMedia add = MapstructUtils.convert(bo, ArStepMedia.class);
|
||
|
||
// 自动设置上传时间
|
||
if (add.getUploadTime() == null) {
|
||
add.setUploadTime(new Date());
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
if (flag) {
|
||
bo.setId(add.getId());
|
||
}
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
### 7.5 唯一性校验方案
|
||
|
||
#### 7.5.1 单字段唯一性校验
|
||
|
||
```java
|
||
private void validEntityBeforeSave(ArDevice entity) {
|
||
// 校验设备编号唯一性
|
||
if (StringUtils.isNotBlank(entity.getDeviceNo())) {
|
||
LambdaQueryWrapper<ArDevice> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArDevice::getDeviceNo, entity.getDeviceNo());
|
||
// 编辑时排除自身
|
||
lqw.ne(entity.getId() != null, ArDevice::getId, entity.getId());
|
||
|
||
long count = baseMapper.selectCount(lqw);
|
||
if (count > 0) {
|
||
throw new ServiceException("设备编号已存在!");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 7.5.2 联合唯一性校验
|
||
|
||
```java
|
||
private void validEntityBeforeSave(ArPoint entity) {
|
||
// 校验点位代码在同一区域内唯一
|
||
if (entity.getRegionId() != null && StringUtils.isNotBlank(entity.getPointCode())) {
|
||
LambdaQueryWrapper<ArPoint> lqw = Wrappers.lambdaQuery();
|
||
lqw.eq(ArPoint::getRegionId, entity.getRegionId());
|
||
lqw.eq(ArPoint::getPointCode, entity.getPointCode());
|
||
lqw.ne(entity.getId() != null, ArPoint::getId, entity.getId());
|
||
|
||
long count = baseMapper.selectCount(lqw);
|
||
if (count > 0) {
|
||
throw new ServiceException("该区域下点位代码已存在!");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 八、部署说明
|
||
|
||
### 8.1 开发环境部署
|
||
|
||
#### 8.1.1 后端启动
|
||
|
||
```bash
|
||
# 1. 编译项目
|
||
mvn clean install -DskipTests
|
||
|
||
# 2. 运行主应用(开发环境)
|
||
cd ruoyi-admin
|
||
mvn spring-boot:run
|
||
|
||
# 或者使用IDE运行
|
||
# 主类:org.dromara.DromaraApplication
|
||
```
|
||
|
||
访问地址:`http://localhost:8080`
|
||
|
||
#### 8.1.2 前端启动
|
||
|
||
```bash
|
||
# 1. 进入前端目录
|
||
cd plus-ui
|
||
|
||
# 2. 安装依赖(首次)
|
||
npm install --registry=https://registry.npmmirror.com
|
||
|
||
# 3. 启动开发服务器
|
||
npm run dev
|
||
```
|
||
|
||
访问地址:`http://localhost:80`
|
||
|
||
默认账号:`admin / admin123`
|
||
|
||
#### 8.1.3 数据库初始化
|
||
|
||
1. 创建数据库:`ry-vue-plus`
|
||
2. 执行脚本:`script/sql/mysql/ry_all_5.5.1.sql`
|
||
3. 执行业务表创建语句(已通过代码自动创建)
|
||
|
||
### 8.2 生产环境部署
|
||
|
||
#### 8.2.1 后端打包
|
||
|
||
```bash
|
||
# 使用生产环境配置打包
|
||
mvn clean package -Pprod -DskipTests
|
||
|
||
# 生成的jar包位置
|
||
# ruoyi-admin/target/ruoyi-admin.jar
|
||
```
|
||
|
||
#### 8.2.2 后端运行
|
||
|
||
```bash
|
||
# 启动应用
|
||
java -jar ruoyi-admin.jar
|
||
|
||
# 指定配置文件
|
||
java -jar ruoyi-admin.jar --spring.profiles.active=prod
|
||
|
||
# 后台运行
|
||
nohup java -jar ruoyi-admin.jar > server.log 2>&1 &
|
||
```
|
||
|
||
#### 8.2.3 前端打包
|
||
|
||
```bash
|
||
cd plus-ui
|
||
|
||
# 构建生产环境
|
||
npm run build:prod
|
||
|
||
# 生成的静态文件位置
|
||
# plus-ui/dist/
|
||
```
|
||
|
||
#### 8.2.4 Nginx配置
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name your-domain.com;
|
||
|
||
# 前端静态文件
|
||
location / {
|
||
root /var/www/ar-inspection/dist;
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
# 后端API代理
|
||
location /prod-api/ {
|
||
proxy_pass http://localhost:8080/;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 8.3 Docker部署
|
||
|
||
#### 8.3.1 Dockerfile(后端)
|
||
|
||
```dockerfile
|
||
FROM openjdk:17-jdk-alpine
|
||
WORKDIR /app
|
||
COPY ruoyi-admin/target/ruoyi-admin.jar app.jar
|
||
EXPOSE 8080
|
||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||
```
|
||
|
||
#### 8.3.2 Dockerfile(前端)
|
||
|
||
```dockerfile
|
||
FROM nginx:alpine
|
||
COPY plus-ui/dist /usr/share/nginx/html
|
||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||
EXPOSE 80
|
||
```
|
||
|
||
#### 8.3.3 docker-compose.yml
|
||
|
||
```yaml
|
||
version: '3'
|
||
services:
|
||
mysql:
|
||
image: mysql:8.0
|
||
environment:
|
||
MYSQL_ROOT_PASSWORD: password
|
||
MYSQL_DATABASE: ry-vue-plus
|
||
ports:
|
||
- "3306:3306"
|
||
volumes:
|
||
- mysql-data:/var/lib/mysql
|
||
|
||
redis:
|
||
image: redis:7-alpine
|
||
ports:
|
||
- "6379:6379"
|
||
|
||
backend:
|
||
build: .
|
||
ports:
|
||
- "8080:8080"
|
||
depends_on:
|
||
- mysql
|
||
- redis
|
||
environment:
|
||
SPRING_PROFILES_ACTIVE: prod
|
||
|
||
frontend:
|
||
build:
|
||
context: .
|
||
dockerfile: Dockerfile.frontend
|
||
ports:
|
||
- "80:80"
|
||
depends_on:
|
||
- backend
|
||
|
||
volumes:
|
||
mysql-data:
|
||
```
|
||
|
||
### 8.4 环境配置检查清单
|
||
|
||
- [ ] MySQL 5.7+ 已安装并启动
|
||
- [ ] Redis 已安装并启动
|
||
- [ ] JDK 17/21 已安装
|
||
- [ ] Maven 3.6+ 已安装(开发环境)
|
||
- [ ] Node.js 18+ 已安装(开发环境)
|
||
- [ ] 数据库连接信息已配置
|
||
- [ ] Redis连接信息已配置
|
||
- [ ] 多租户已禁用(tenant.enable: false)
|
||
- [ ] 所有业务表已加入tenant.excludes
|
||
- [ ] 文件上传路径已配置
|
||
- [ ] 日志路径已配置
|
||
|
||
---
|
||
|
||
## 九、系统测试
|
||
|
||
### 9.1 功能测试清单
|
||
|
||
#### 9.1.1 AR设备管理
|
||
- [ ] 新增设备(设备编号唯一性校验)
|
||
- [ ] 修改设备信息
|
||
- [ ] 删除设备
|
||
- [ ] 查询设备列表(分页、条件查询)
|
||
- [ ] 导出设备列表Excel
|
||
|
||
#### 9.1.2 区域管理
|
||
- [ ] 新增区域(区域代码唯一性校验)
|
||
- [ ] 修改区域信息(包含region_data JSON)
|
||
- [ ] 删除区域
|
||
- [ ] 查询区域列表
|
||
- [ ] 导出区域列表
|
||
|
||
#### 9.1.3 点位管理
|
||
- [ ] 新增点位(点位代码区域内唯一性校验)
|
||
- [ ] 修改点位(包含position_data JSON)
|
||
- [ ] 删除点位
|
||
- [ ] 查询点位列表
|
||
- [ ] 导出点位列表
|
||
|
||
#### 9.1.4 任务模板管理
|
||
- [ ] 新增任务(任务代码唯一性校验)
|
||
- [ ] 修改任务
|
||
- [ ] 删除任务
|
||
- [ ] 查询任务列表
|
||
- [ ] 导出任务列表
|
||
|
||
#### 9.1.5 步骤管理
|
||
- [ ] 新增根步骤(parentId=0)
|
||
- [ ] 新增子步骤(ancestors自动维护)
|
||
- [ ] 修改步骤(包含ai_data JSON)
|
||
- [ ] 删除步骤(级联删除子步骤)
|
||
- [ ] 查询步骤树形结构
|
||
- [ ] 查询步骤列表
|
||
- [ ] 导出步骤列表
|
||
|
||
#### 9.1.6 任务执行管理
|
||
- [ ] 新增执行记录(execution_code自动生成)
|
||
- [ ] 修改状态为in_progress(start_time自动设置)
|
||
- [ ] 修改状态为completed(end_time自动设置)
|
||
- [ ] 修改状态为cancelled
|
||
- [ ] 删除执行记录
|
||
- [ ] 查询执行记录列表
|
||
- [ ] 导出执行记录
|
||
|
||
#### 9.1.7 步骤记录管理
|
||
- [ ] 新增步骤记录
|
||
- [ ] 修改步骤记录
|
||
- [ ] 完成步骤(completion_time和duration自动计算)
|
||
- [ ] 删除步骤记录
|
||
- [ ] 查询步骤记录列表
|
||
- [ ] 导出步骤记录
|
||
|
||
#### 9.1.8 媒体文件管理
|
||
- [ ] 新增媒体文件(upload_time自动设置)
|
||
- [ ] 修改媒体文件信息
|
||
- [ ] 删除媒体文件
|
||
- [ ] 查询媒体文件列表
|
||
- [ ] 导出媒体文件列表
|
||
|
||
### 9.2 性能测试建议
|
||
|
||
- 步骤树形结构查询性能(大数据量)
|
||
- 分页查询性能
|
||
- JSON字段查询性能
|
||
- 级联删除性能
|
||
|
||
### 9.3 安全测试建议
|
||
|
||
- 权限控制测试(Sa-Token)
|
||
- SQL注入防护测试
|
||
- XSS防护测试
|
||
- 文件上传安全测试
|
||
|
||
---
|
||
|
||
## 十、附录
|
||
|
||
### 10.1 权限标识清单
|
||
|
||
| 模块 | 权限标识 |
|
||
|------|---------|
|
||
| AR设备管理 | inspection:device:list / query / add / edit / remove / export |
|
||
| 区域管理 | inspection:region:list / query / add / edit / remove / export |
|
||
| 点位管理 | inspection:point:list / query / add / edit / remove / export |
|
||
| 任务模板管理 | inspection:task:list / query / add / edit / remove / export |
|
||
| 步骤管理 | inspection:step:list / query / add / edit / remove / export |
|
||
| 任务执行管理 | inspection:execution:list / query / add / edit / remove / export |
|
||
| 步骤记录管理 | inspection:stepRecord:list / query / add / edit / remove / export |
|
||
| 媒体文件管理 | inspection:stepMedia:list / query / add / edit / remove / export |
|
||
|
||
### 10.2 数据字典
|
||
|
||
#### 设备状态(device.status)
|
||
- 0:正常
|
||
- 1:停用
|
||
|
||
#### 执行状态(execution.status)
|
||
- pending:待执行
|
||
- in_progress:执行中
|
||
- completed:已完成
|
||
- cancelled:已取消
|
||
|
||
#### 步骤记录状态(step_record.status)
|
||
- pending:待执行
|
||
- completed:已完成
|
||
- skipped:已跳过
|
||
|
||
#### 媒体类型(step_media.media_type)
|
||
- image:图片
|
||
- video:视频
|
||
- audio:音频
|
||
|
||
#### 是否标识(通用)
|
||
- 0:否
|
||
- 1:是
|
||
|
||
### 10.3 常见问题FAQ
|
||
|
||
**Q1: 为什么不使用多租户?**
|
||
A: 根据项目实际需求,系统面向单一组织使用,不需要多租户隔离,禁用多租户可以简化数据模型和查询逻辑。
|
||
|
||
**Q2: JSON字段如何进行查询?**
|
||
A: MyBatis-Plus支持JSON字段的基本查询,复杂查询可以使用原生SQL或MySQL的JSON函数。
|
||
|
||
**Q3: 步骤树形结构支持多深?**
|
||
A: 理论上支持无限深度,但建议不超过5层,过深的层级会影响查询性能和用户体验。
|
||
|
||
**Q4: 如何批量导入设备/区域等数据?**
|
||
A: 使用Excel导入功能(需要前端实现),或通过API批量调用新增接口。
|
||
|
||
**Q5: 媒体文件存储在哪里?**
|
||
A: 系统支持MinIO、阿里云OSS等对象存储,file_url存储访问地址。
|
||
|
||
### 10.4 后续优化建议
|
||
|
||
1. **性能优化**
|
||
- 为常用查询字段添加索引
|
||
- 步骤树形结构考虑使用缓存
|
||
- 大数据量导出使用异步任务
|
||
|
||
2. **功能增强**
|
||
- 步骤执行过程的WebSocket实时推送
|
||
- 任务执行进度可视化
|
||
- 执行报告自动生成
|
||
- 数据统计分析
|
||
|
||
3. **安全加固**
|
||
- 文件上传类型限制
|
||
- 文件大小限制
|
||
- 敏感数据加密
|
||
- 操作审计日志
|
||
|
||
4. **运维监控**
|
||
- 接口性能监控
|
||
- 异常告警
|
||
- 数据备份策略
|
||
- 日志归档策略
|
||
|
||
---
|
||
|
||
## 文档变更记录
|
||
|
||
| 版本 | 日期 | 变更内容 | 作者 |
|
||
|------|------|---------|------|
|
||
| 1.0 | 2025-01-13 | 初始版本,完成全部设计文档 | Claude Code |
|
||
|
||
---
|
||
|
||
**文档结束**
|