Compare commits

...

11 Commits

Author SHA1 Message Date
aa763a0ee6 feat: publish front end 2025-12-11 13:45:30 +08:00
7a8f25cf65 feat: new api 2025-12-10 18:34:03 +08:00
ab86729e33 feat: new api 2025-12-10 18:05:25 +08:00
f9eaa441a3 feat: opt admin web 2025-12-02 15:17:50 +08:00
891bffba3b feat: 精简登录页面
- 使用纯色背景替代背景图片 (#28273a)
- 隐藏第三方登录入口 (使用v-if保留逻辑)
- 修改底部 Copyright 为 NJCQ Tech

参考项目: otc-boot/plus-ui-ts
2025-12-02 15:12:20 +08:00
07f8ef90bb feat: UI定制 - 精简导航栏和禁用深色模式
- 删除右上角GitHub、文档、全屏、语言、大小图标
- 隐藏消息入口(使用v-if保留逻辑)
- 移除设置面板的深色模式开关
- 注释后端登录欢迎消息推送

参考项目: otc-boot/plus-ui-ts
2025-12-02 15:05:04 +08:00
e076e42b86 feat: opt admin web 2025-12-02 14:41:51 +08:00
fbfe6c30f9 feat: ready to reply 2025-12-02 14:09:31 +08:00
839e0617ee feat: update claude files 2025-12-02 13:39:17 +08:00
ac0a17a943 Merge branch 'main' of ssh://git.njcqit.com:2222/eric/ar-inspection 2025-12-02 13:31:38 +08:00
9ed58de1ce feat: admin web dev 2025-12-02 13:31:25 +08:00
27 changed files with 1778 additions and 813 deletions

View File

@@ -1,11 +0,0 @@
{
"permissions": {
"allow": [
"Bash(tree:*)",
"Bash(mvn clean package:*)",
"Bash(echo:*)"
],
"deny": [],
"ask": []
}
}

View File

@@ -3,7 +3,10 @@
"allow": [
"Bash(tree:*)",
"Bash(mvn clean package:*)",
"Bash(echo:*)"
"Bash(echo:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(grep:*)"
],
"deny": [],
"ask": []

384
CLAUDE.md
View File

@@ -142,95 +142,12 @@ plus-ui/
4. **HTTP 客户端**: Axios 1.8.4
5. **表格组件**: vxe-table 4.13.7
6. **接口加密**: RSA + AES 动态加密
7. **原子化CSS**: UnoCSS
7. **原子化CSS**: UnoCSS 66.5.2
## 项目特定技术规范
## 常用命令
### 后端开发规范
### 后端开发
1. **模块结构**:
- `ruoyi-admin/` - 主应用入口,负责启动和全局配置
- `ruoyi-common/` - 通用功能模块(不要随意修改)
- `ruoyi-modules/` - 业务模块目录
- `ruoyi-system/` - 系统管理模块
- `ruoyi-inspection/` - AR巡检核心业务模块
- 其他业务模块按功能划分
2. **代码分层**:
```
controller/ # 控制器层,处理HTTP请求
service/ # 业务逻辑层接口
service/impl/ # 业务逻辑实现
mapper/ # 数据访问层
domain/ # 实体类
vo/ # 视图对象
bo/ # 业务对象
```
3. **必须遵循的规范**:
- 实体类必须继承 `BaseEntity` 并使用 Lombok 注解
- Mapper 接口继承 `BaseMapperPlus<实体类Mapper, 实体类, VO类>`
- Service 实现类使用 `@RequiredArgsConstructor` 注入依赖
- Controller 统一返回 `R<T>` 类型
- 使用 `@SaCheckPermission` 进行权限控制
- 所有 API 添加 Swagger 注解: `@Tag`, `@Operation`, `@Parameters`
4. **命名约定**:
- 实体类: `XxxEntity` 或直接 `Xxx`
- Mapper: `XxxMapper`
- Service: `IXxxService` (接口) / `XxxServiceImpl` (实现)
- Controller: `XxxController`
- VO: `XxxVo`
- BO: `XxxBo`
5. **数据库操作**:
- 优先使用 MyBatis-Plus 的内置方法
- 复杂查询在 Mapper XML 中编写
- 使用 `LambdaQueryWrapper` 构建动态查询
- 分页使用 `TableDataInfo<T>` 和 `PageQuery`
### 前端开发规范
1. **目录结构**:
```
plus-ui/
├── src/
│ ├── api/ # API接口定义
│ ├── views/ # 页面视图
│ ├── components/ # 可复用组件
│ ├── store/ # Pinia状态管理
│ ├── router/ # 路由配置
│ ├── utils/ # 工具函数
│ └── types/ # TypeScript类型定义
```
2. **组件开发**:
- 使用 Vue 3 Composition API (`<script setup lang="ts">`)
- 优先使用 Element Plus 组件
- 表单使用 `el-form` + 表单验证规则
- 表格使用 `vxe-table` 或 `el-table`
- 使用 `useRouter`, `useRoute` 进行路由操作
3. **API 调用规范**:
- API 定义在 `src/api/` 目录,按模块分文件
- 使用 `request` 工具发起请求
- 统一错误处理,使用 `ElMessage` 显示提示
- 类型定义使用 TypeScript interface
4. **状态管理**:
- 使用 Pinia 管理全局状态
- Store 文件放在 `src/store/modules/`
- 使用组合式 API: `defineStore`
5. **样式规范**:
- 使用 `<style scoped lang="scss">`
- 优先使用 UnoCSS 原子类
- 遵循 BEM 命名规范
- 响应式布局使用 Element Plus 的栅格系统
### 常用命令
**后端开发**:
```bash
# 构建项目(跳过测试)
mvn clean install -DskipTests
@@ -245,7 +162,8 @@ mvn test
mvn clean package -Pprod -DskipTests
```
**前端开发**:
### 前端开发
```bash
# 安装依赖
cd plus-ui && npm install --registry=https://registry.npmmirror.com
@@ -265,12 +183,12 @@ npm run prettier
### 自定义斜杠命令
- `/build` - 构建整个 Maven 项目
- `/start-backend` - 启动后端 Spring Boot 服务
- `/build` - 构建整个 Maven 项目并运行测试
- `/start-backend` - 启动后端 Spring Boot 应用
- `/start-frontend` - 启动前端 Vue3 开发服务器
- `/new-module` - 创建新的业务模块(Controller, Service, Mapper, Domain)
- `/analyze` - 分析项目结构并生成架构文档
- `/lint` - 运行前端代码检查和格式化
- `/new-module` - 创建新的业务模块脚手架
- `/analyze` - 分析项目架构和依赖关系
## 配置说明
@@ -344,6 +262,67 @@ VITE_APP_CONTEXT_PATH = /
- 支持本部门、本部门及以下、仅本人等权限范围
- 通过 MyBatis-Plus 插件实现
## 开发规范
### 后端代码分层
```
controller/ # 控制器层,处理HTTP请求
service/ # 业务逻辑层接口
service/impl/ # 业务逻辑实现
mapper/ # 数据访问层
domain/ # 实体类
vo/ # 视图对象
bo/ # 业务对象
```
### 必须遵循的规范
1. **实体类**: 继承 `BaseEntity` 并使用 Lombok 注解
2. **Mapper 接口**: 继承 `BaseMapperPlus<实体类Mapper, 实体类, VO类>`
3. **Service 实现**: 使用 `@RequiredArgsConstructor` 注入依赖
4. **Controller 返回**: 统一返回 `R<T>` 类型
5. **权限控制**: 使用 `@SaCheckPermission` 注解
6. **API 文档**: 添加 `@Tag`, `@Operation`, `@Parameters` 注解
### 命名约定
- 实体类: `XxxEntity` 或直接 `Xxx`
- Mapper: `XxxMapper`
- Service: `IXxxService` (接口) / `XxxServiceImpl` (实现)
- Controller: `XxxController`
- VO: `XxxVo`
- BO: `XxxBo`
### 数据库操作
- 优先使用 MyBatis-Plus 的内置方法
- 复杂查询在 Mapper XML 中编写
- 使用 `LambdaQueryWrapper` 构建动态查询
- 分页使用 `TableDataInfo<T>``PageQuery`
### 前端开发规范
1. **组件开发**:
- 使用 Vue 3 Composition API (`<script setup lang="ts">`)
- 优先使用 Element Plus 组件
- 表单使用 `el-form` + 表单验证规则
- 表格使用 `vxe-table``el-table`
2. **API 调用**:
- API 定义在 `src/api/` 目录,按模块分文件
- 使用 `request` 工具发起请求
- 统一错误处理,使用 `ElMessage` 显示提示
3. **状态管理**:
- 使用 Pinia 管理全局状态
- Store 文件放在 `src/store/modules/`
4. **样式规范**:
- 使用 `<style scoped lang="scss">`
- 优先使用 UnoCSS 原子类
- 响应式布局使用 Element Plus 栅格系统
## 监控与运维
### 服务端口
@@ -385,95 +364,33 @@ VITE_APP_CONTEXT_PATH = /
- 符合项目规范的代码风格
- 生成后需根据业务需求调整
### 项目特定注意事项
## 项目特定注意事项
1. **AR 巡检业务模块** (`ruoyi-inspection`):
- 核心业务逻辑,修改需谨慎
- 涉及设备点位、巡检任务、缺陷记录等核心功能
- 修改前先阅读业务设计文档: `AR-INSPECTION-DESIGN.md`
### AR 巡检业务模块 (`ruoyi-inspection`)
2. **权限控制**:
- 使用 Sa-Token 进行权限认证
- 权限字符串格式: `模块:功能:操作` (如 `system:user:add`)
- 菜单权限在数据库 `sys_menu` 表管理
- **核心业务逻辑**: 位于 `ruoyi-modules/ruoyi-inspection`
- **主要功能**: 设备点位管理、巡检任务、缺陷记录、AR 技术集成
- **修改建议**: 涉及核心业务,修改需谨慎
- **依赖模块**: 依赖所有 ruoyi-common 核心模块
3. **文件存储**:
- 使用 OSS 进行文件存储(支持 MinIO, 阿里云OSS等)
- 配置在 `application-*.yml` 中的 `oss` 节点
### 权限控制
4. **多租户支持**:
- 框架内置多租户功能
- 通过 `@TenantIgnore` 注解排除租户过滤
- 使用 Sa-Token 进行权限认证
- 权限字符串格式: `模块:功能:操作` (如 `system:user:add`)
- 菜单权限在数据库 `sys_menu` 表管理
- 使用 `@SaCheckPermission("system:user:list")` 注解控制接口权限
5. **代码生成器**:
- 访问 http://localhost:8080/tool/gen
- 可快速生成 CRUD 代码
- 生成后需根据业务需求调整
### 文件存储
### 问题排查
- 使用 OSS 进行文件存储(支持 MinIO, 阿里云OSS等)
- 配置在 `application-*.yml` 中的 `oss` 节点
- 统一的文件上传下载接口
1. **后端启动失败**:
- 检查数据库连接配置
- 检查 Redis 是否启动
- 查看日志: `logs/sys-console.log`
### 多租户支持
2. **前端启动失败**:
- 删除 `node_modules` 重新安装
- 检查 Node.js 版本 >= 18.18.0
- 检查端口 80 是否被占用
3. **接口调用失败**:
- 检查后端服务是否启动
- 检查跨域配置
- 查看浏览器控制台和网络请求
## 框架技术注意事项
### 架构特点
1. **插件化架构**
- 各 `ruoyi-common-*` 模块相互独立
- 可按需引入功能模块
- 易于扩展和维护
2. **编码规范**
- 严格遵守 Alibaba Java 编码规范
- 使用 Lombok 简化代码
- IDE 需要安装 Lombok 插件
3. **对象转换**
- 使用 MapStruct-Plus 进行对象转换
- 避免使用 BeanUtils.copyProperties
- 性能优于反射方式
### 技术选型说明
1. **数据库连接池**: HikariCP (非 Druid)
- 性能更优
- Spring Boot 默认连接池
2. **Web 容器**: Undertow (非 Tomcat)
- 非阻塞 IO
- 内存占用更小
- 高并发性能更好
3. **JSON 序列化**: Jackson (非 Fastjson)
- 安全性更高
- Spring Boot 默认选择
4. **接口加密**
- 前后端需同时开启/关闭
- RSA + AES 混合加密
- 开发环境建议关闭
### 测试
- **单元测试**: JUnit 5 + Spring Boot Test
- **运行测试**: `mvn test`
- **测试分组**: 通过 `@Tag` 注解标记,根据环境执行
- `@Tag("dev")` - 开发环境测试
- `@Tag("prod")` - 生产环境测试
- `@Tag("exclude")` - 排除的测试
- 框架内置多租户功能
- 通过 `@TenantIgnore` 注解排除租户过滤
- 租户隔离在数据库层面自动实现
### Docker 部署
@@ -488,108 +405,41 @@ docker-compose up -d
docker-compose down
```
## Universal Development Guidelines
## 测试
### Code Quality Standards
- Write clean, readable, and maintainable code
- Follow consistent naming conventions across the project
- Use meaningful variable and function names
- Keep functions focused and single-purpose
- Add comments for complex logic and business rules
- **单元测试**: JUnit 5 + Spring Boot Test
- **运行测试**: `mvn test`
- **测试分组**: 通过 `@Tag` 注解标记,根据环境执行
- `@Tag("dev")` - 开发环境测试
- `@Tag("prod")` - 生产环境测试
- `@Tag("exclude")` - 排除的测试
### Git Workflow
- Use descriptive commit messages following conventional commits format
- Create feature branches for new development
- Keep commits atomic and focused on single changes
- Use pull requests for code review before merging
- Maintain a clean commit history
## 问题排查
### Documentation
- Keep README.md files up to date
- Document public APIs and interfaces
- Include usage examples for complex features
- Maintain inline code documentation
- Update documentation when making changes
### 后端启动失败
### Testing Approach
- Write tests for new features and bug fixes
- Maintain good test coverage
- Use descriptive test names that explain the expected behavior
- Organize tests logically by feature or module
- Run tests before committing changes
- 检查数据库连接配置
- 检查 Redis 是否启动
- 查看日志: `logs/sys-console.log`
### Security Best Practices
- Never commit sensitive information (API keys, passwords, tokens)
- Use environment variables for configuration
- Validate input data and sanitize outputs
- Follow principle of least privilege
- Keep dependencies updated
### 前端启动失败
## Project Structure Guidelines
- 删除 `node_modules` 重新安装
- 检查 Node.js 版本 >= 18.18.0
- 检查端口 80 是否被占用
### File Organization
- Group related files in logical directories
- Use consistent file and folder naming conventions
- Separate source code from configuration files
- Keep build artifacts out of version control
- Organize assets and resources appropriately
### 接口调用失败
### Configuration Management
- Use configuration files for environment-specific settings
- Centralize configuration in dedicated files
- Use environment variables for sensitive or environment-specific data
- Document configuration options and their purposes
- Provide example configuration files
- 检查后端服务是否启动
- 检查跨域配置
- 查看浏览器控制台和网络请求
## Development Workflow
## 框架技术注意事项
### Before Starting Work
1. Pull latest changes from main branch
2. Create a new feature branch
3. Review existing code and architecture
4. Plan the implementation approach
### During Development
1. Make incremental commits with clear messages
2. Run tests frequently to catch issues early
3. Follow established coding standards
4. Update documentation as needed
### Before Submitting
1. Run full test suite
2. Check code quality and formatting
3. Update documentation if necessary
4. Create clear pull request description
## Common Patterns
### Error Handling
- Use appropriate error handling mechanisms for the language
- Provide meaningful error messages
- Log errors appropriately for debugging
- Handle edge cases gracefully
- Don't expose sensitive information in error messages
### Performance Considerations
- Profile code for performance bottlenecks
- Optimize database queries and API calls
- Use caching where appropriate
- Consider memory usage and resource management
- Monitor and measure performance metrics
### Code Reusability
- Extract common functionality into reusable modules
- Use dependency injection for better testability
- Create utility functions for repeated operations
- Design interfaces for extensibility
- Follow DRY (Don't Repeat Yourself) principle
## Review Checklist
Before marking any task as complete:
- [ ] Code follows established conventions
- [ ] Tests are written and passing
- [ ] Documentation is updated
- [ ] Security considerations are addressed
- [ ] Performance impact is considered
- [ ] Code is reviewed for maintainability
1. **插件化架构**: 各 `ruoyi-common-*` 模块相互独立,可按需引入
2. **编码规范**: 严格遵守 Alibaba Java 编码规范
3. **对象转换**: 使用 MapStruct-Plus 进行对象转换
4. **数据库连接池**: HikariCP (非 Druid)
5. **Web 容器**: Undertow (非 Tomcat)
6. **JSON 序列化**: Jackson (非 Fastjson)
7. **接口加密**: 前后端需同时开启/关闭

View File

@@ -1,273 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## 项目概述
这是一个基于 RuoYi-Vue-Plus 5.5.1 的分布式多租户管理系统,采用前后端分离架构:
- **后端**: Spring Boot 3.5.7 + JDK 17/21 + MyBatis-Plus
- **前端**: Vue 3 + TypeScript + Element Plus + Vite
## 开发环境要求
### 后端
- JDK 17 或 JDK 21
- Maven 3.6+
- MySQL 5.7+ / Oracle / PostgreSQL / SQL Server
### 前端
- Node.js >= 18.18.0
- npm >= 8.9.0
## 常用命令
### 后端开发
```bash
# 编译项目(跳过测试)
mvn clean install -DskipTests
# 运行项目(默认 dev 环境)
mvn spring-boot:run
# 运行项目(指定环境)
mvn spring-boot:run -Plocal
mvn spring-boot:run -Pprod
# 运行单元测试
mvn test
# 打包生产环境
mvn clean package -Pprod
# 主应用入口
# ruoyi-admin/src/main/java/org/dromara/DromaraApplication.java
```
### 前端开发
```bash
# 进入前端目录
cd plus-ui
# 安装依赖
npm install --registry=https://registry.npmmirror.com
# 启动开发服务器 (http://localhost:80)
npm run dev
# 构建生产环境
npm run build:prod
# 构建开发环境
npm run build:dev
# 代码检查和修复
npm run lint:eslint:fix
# 代码格式化
npm prettier
```
### Docker 部署
```bash
# 使用 docker-compose 启动所有服务(MySQL + Redis + Nginx 等)
cd script/docker
docker-compose up -d
# 停止所有服务
docker-compose down
```
## 项目架构
### 后端模块结构
```
ruoyi-vue-plus/
├── ruoyi-admin/ # 主应用模块,Web服务入口
├── ruoyi-common/ # 通用模块(插件化架构)
│ ├── ruoyi-common-core/ # 核心模块
│ ├── ruoyi-common-mybatis/ # MyBatis-Plus 集成
│ ├── ruoyi-common-security/ # Sa-Token 安全认证
│ ├── ruoyi-common-oss/ # 对象存储(S3/Minio)
│ ├── ruoyi-common-doc/ # SpringDoc API文档
│ ├── ruoyi-common-redis/ # Redis 缓存
│ ├── ruoyi-common-job/ # SnailJob 定时任务
│ ├── ruoyi-common-json/ # Jackson 序列化
│ ├── ruoyi-common-log/ # 操作日志
│ ├── ruoyi-common-web/ # Web 配置
│ ├── ruoyi-common-translation/# 数据翻译
│ ├── ruoyi-common-encrypt/ # 数据加解密
│ ├── ruoyi-common-sensitive/ # 数据脱敏
│ ├── ruoyi-common-idempotent/ # 幂等处理
│ ├── ruoyi-common-ratelimiter/# 限流
│ ├── ruoyi-common-social/ # 第三方登录
│ ├── ruoyi-common-sms/ # 短信服务
│ ├── ruoyi-common-mail/ # 邮件服务
│ └── ruoyi-common-websocket/ # WebSocket/SSE
├── ruoyi-modules/ # 业务模块
│ ├── ruoyi-system/ # 系统管理模块
│ ├── ruoyi-generator/ # 代码生成器
│ ├── ruoyi-demo/ # 演示案例
│ ├── ruoyi-workflow/ # 工作流模块(Warm-Flow)
│ └── ruoyi-job/ # 任务调度
└── ruoyi-extend/ # 扩展模块
├── ruoyi-monitor-admin/ # SpringBoot Admin 监控
└── ruoyi-snailjob-server/ # SnailJob 调度中心
```
### 前端目录结构
```
plus-ui/
├── src/
│ ├── api/ # API 接口定义
│ │ ├── demo/ # 演示模块
│ │ ├── monitor/ # 监控模块
│ │ ├── system/ # 系统管理
│ │ ├── tool/ # 工具模块
│ │ └── workflow/ # 工作流
│ ├── assets/ # 静态资源
│ ├── components/ # 公共组件
│ ├── directive/ # 自定义指令
│ ├── hooks/ # 组合式函数
│ ├── layout/ # 布局组件
│ ├── lang/ # 国际化
│ ├── plugins/ # 插件封装
│ ├── router/ # 路由配置
│ ├── store/ # Pinia 状态管理
│ ├── types/ # TypeScript 类型定义
│ ├── utils/ # 工具函数
│ └── views/ # 页面视图
├── vite/ # Vite 插件配置
└── vite.config.ts # Vite 配置文件
```
## 核心技术架构
### 后端核心组件
1. **权限认证**: Sa-Token + JWT (非 Spring Security)
- 支持登录校验、角色校验、权限校验、二级认证等
- 支持复杂权限表达式 (AND/OR)
2. **ORM 框架**: MyBatis-Plus
- 雪花ID主键 (ASSIGN_ID)
- 多租户插件 (默认启用)
- 数据权限插件
- 分页插件
3. **缓存方案**: Redisson (非 Lettuce)
- 支持分布式锁 (Lock4j)
- 支持 Spring Cache 注解
4. **多数据源**: Dynamic-Datasource
- 支持异构数据库动态切换
5. **任务调度**: SnailJob (非 Quartz)
- 分布式任务调度
- 支持分片、重试、DAG 任务流
6. **工作流引擎**: Warm-Flow
- 国产工作流引擎
- 支持复杂审批流程
7. **文件存储**: MinIO / AWS S3
- 支持七牛、阿里云、腾讯云等
8. **API 文档**: SpringDoc (非 Springfox)
- 基于 javadoc 注释自动生成
- 零注解入侵
### 前端核心特性
1. **UI 框架**: Element Plus
2. **状态管理**: Pinia (非 Vuex)
3. **路由**: Vue Router 4
4. **HTTP 客户端**: Axios
5. **表格组件**: vxe-table
6. **接口加密**: RSA + AES 动态加密
## 配置说明
### 后端配置
- **主配置文件**: `ruoyi-admin/src/main/resources/application.yml`
- **环境配置**:
- `application-dev.yml` (开发)
- `application-prod.yml` (生产)
- `application-local.yml` (本地)
- **多环境切换**: 通过 Maven Profile 切换
```xml
<profiles.active>dev|prod|local</profiles.active>
```
### 前端配置
- **环境变量**: `.env.development` / `.env.production`
- **代理配置**: `vite.config.ts` 中配置后端代理
- 默认代理到 `http://localhost:8080`
### 重要配置项
1. **多租户**: `tenant.enable=true` (默认开启)
2. **接口加密**: `api-decrypt.enabled=true`
3. **数据加密**: `mybatis-encryptor.enable=false` (默认关闭)
4. **WebSocket**: 默认关闭,推荐使用 SSE
5. **验证码**: `captcha.enable=true`
## 数据库说明
- **主键策略**: 雪花ID (ASSIGN_ID),不使用数据库自增
- **逻辑删除**: 默认启用 (`mybatis-plus.enableLogicDelete=true`)
- **多租户表**: 自动添加 `tenant_id` 字段 (排除表在配置中指定)
## 代码生成器
位于系统管理 -> 代码生成模块:
- 支持多数据源代码生成
- 自动生成 Controller、Service、Mapper、Vue 页面
- 符合项目规范的代码风格
## 监控与运维
1. **应用监控**: Spring Boot Admin
- 访问地址: `http://localhost:9090/admin`
- 用户名/密码: 配置文件中设置
2. **任务调度中心**: SnailJob
- 访问地址: `http://localhost:8800/snail-job`
3. **API 文档**: SpringDoc
- 开发环境访问: `http://localhost:8080/doc.html`
4. **日志**: Logback
- 日志路径: `./logs/sys-console.log`
## 测试
- **单元测试**: 使用 JUnit 5 + Spring Boot Test
- **运行测试**: `mvn test`
- **测试分组**: 通过 `@Tag` 注解标记,根据环境执行
## 注意事项
1. 项目采用插件化架构,各 `ruoyi-common-*` 模块相互独立,易于扩展
2. 严格遵守 Alibaba Java 编码规范
3. 使用 Lombok 简化代码,需要IDE安装 Lombok 插件
4. 使用 MapStruct-Plus 进行对象转换
5. 前端使用 TypeScript,需要注意类型定义
6. 接口加密功能前后端需同时开启/关闭
7. 数据库连接池使用 HikariCP (非 Druid)
8. Web 容器使用 Undertow (非 Tomcat)
## 部署端口
- 后端应用: 8080
- 前端应用: 80
- SnailJob 客户端: 28080
- Spring Boot Admin: 9090
- SnailJob Server: 8800

View File

@@ -1,6 +1,6 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
VITE_APP_TITLE = AR巡检管理平台
VITE_APP_LOGO_TITLE = AR巡检管理平台
# 开发环境配置
VITE_APP_ENV = 'development'

View File

@@ -1,6 +1,6 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_LOGO_TITLE = RuoYi-Vue-Plus
VITE_APP_TITLE = AR巡检管理平台
VITE_APP_LOGO_TITLE = AR巡检管理平台
# 生产环境配置
VITE_APP_ENV = 'production'

View File

@@ -1,8 +1,8 @@
{
"$schema": "https://json.schemastore.org/package",
"name": "ruoyi-vue-plus",
"version": "5.5.1-2.5.1",
"description": "RuoYi-Vue-Plus多租户管理系统",
"name": "AR巡检管理平台",
"version": "1.0.0",
"description": "AR巡检管理平台",
"author": "LionLi",
"license": "MIT",
"type": "module",
@@ -15,10 +15,6 @@
"lint:eslint:fix": "eslint --fix",
"prettier": "prettier --write ."
},
"repository": {
"type": "git",
"url": "https://gitee.com/JavaLionLi/plus-ui.git"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@highlightjs/vue-plugin": "2.1.0",

View File

@@ -27,8 +27,8 @@
<svg-icon class-name="search-icon" icon-class="search" />
</div>
</el-tooltip>
<!-- 消息 -->
<el-tooltip :content="proxy.$t('navbar.message')" effect="dark" placement="bottom">
<!-- 消息 - 已隐藏 -->
<el-tooltip v-if="false" :content="proxy.$t('navbar.message')" effect="dark" placement="bottom">
<div>
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
<template #reference>
@@ -42,25 +42,6 @@
</el-popover>
</div>
</el-tooltip>
<el-tooltip content="Github" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="proxy.$t('navbar.document')" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="proxy.$t('navbar.full')" effect="dark" placement="bottom">
<screenfull id="screenfull" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="proxy.$t('navbar.language')" effect="dark" placement="bottom">
<lang-select id="lang-select" class="right-menu-item hover-effect" />
</el-tooltip>
<el-tooltip :content="proxy.$t('navbar.layoutSize')" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<div class="avatar-container">
<el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
@@ -281,6 +262,7 @@ watch(
.avatar-container {
margin-right: 40px;
margin-left: 20px;
.avatar-wrapper {
margin-top: 5px;
@@ -290,7 +272,7 @@ watch(
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
border-radius: 20px;
margin-top: 10px;
}

View File

@@ -34,12 +34,6 @@
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
</span>
</div>
<div class="drawer-item">
<span>深色模式</span>
<span class="comp-style">
<el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" />
</span>
</div>
<el-divider />
@@ -114,22 +108,6 @@ const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore);
const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
// 是否暗黑模式
const isDark = useDark({
storageKey: 'useDarkKey',
valueDark: 'dark',
valueLight: 'light'
});
// 匹配菜单颜色
watch(isDark, () => {
if (isDark.value) {
settingsStore.sideTheme = SideThemeEnum.DARK;
} else {
settingsStore.sideTheme = sideTheme.value;
}
});
const toggleDark = () => useToggle(isDark);
const topNavChange = (val: any) => {
if (!val) {
appStore.toggleSideBarHide(false);
@@ -148,11 +126,6 @@ const themeChange = (val: string) => {
};
const handleTheme = (val: string) => {
sideTheme.value = val;
if (isDark.value && val === SideThemeEnum.LIGHT) {
// 暗黑模式颜色不变
settingsStore.sideTheme = SideThemeEnum.DARK;
return;
}
settingsStore.sideTheme = val;
};
const saveSetting = () => {

View File

@@ -1,165 +1,18 @@
<template>
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>RuoYi-Vue-Plus多租户管理系统</h2>
<p>
RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
<br />
* 前端开发框架 Vue3TSElement Plus<br />
* 后端开发框架 Spring Boot<br />
* 容器框架 Undertow 基于 Netty 的高性能容器<br />
* 权限认证框架 Sa-Token 支持多终端认证系统<br />
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
* 缓存数据库 Redis 适配 6.X 最低 4.X<br />
* 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
* 数据库框架 p6spy 更强劲的 SQL 分析<br />
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
* 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
* Redis客户端 Redisson 性能强劲API丰富<br />
* 分布式限流 Redisson 全局请求IP集群ID 多种限流<br />
* 分布式锁 Lock4j 注解锁工具锁 多种多样<br />
* 分布式幂等 Lock4j 基于分布式锁实现<br />
* 分布式链路追踪 SkyWalking 支持链路追踪网格分析度量聚合可视化<br />
* 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
* 文件存储 Minio 本地存储<br />
* 文件存储 七牛阿里腾讯 云存储<br />
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
* 校验框架 Validation 增强接口安全性 严谨性<br />
* Excel框架 FastExcel(原Alibaba EasyExcel) 性能优异 扩展性强<br />
* 文档框架 SpringDocjavadoc 无注解零入侵基于java注释<br />
* 工具类框架 HutoolLombok 减少代码冗余 增加安全性<br />
* 代码生成器 适配MPSpringDoc规范化代码 一键生成前后端代码<br />
* 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br />
</p>
<p><b>当前版本:</b> <span>v5.5.1</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
>更新日志</el-button
>
</p>
</el-col>
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>RuoYi-Cloud-Plus多租户微服务管理系统</h2>
<p>
RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
<br />
* 前端开发框架 Vue3TSElement UI<br />
* 后端开发框架 Spring Boot<br />
* 微服务开发框架 Spring CloudSpring Cloud Alibaba<br />
* 容器框架 Undertow 基于 XNIO 的高性能容器<br />
* 权限认证框架 Sa-TokenJwt 支持多终端认证系统<br />
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
* 关系数据库 Oracle 适配 11g 12c<br />
* 关系数据库 PostgreSQL 适配 13 14<br />
* 关系数据库 SQLServer 适配 2017 2019<br />
* 缓存数据库 Redis 适配 6.X 最低 5.X<br />
* 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
* 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
* 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
* 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
* RPC远程调用 Apache Dubbo 原生态使用体验高性能<br />
* 分布式限流熔断 Alibaba Sentinel 无侵入高扩展<br />
* 分布式事务 Alibaba Seata 无侵入高扩展 支持 四种模式<br />
* 分布式消息队列 Apache Kafka 高性能高速度<br />
* 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
* 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
* 分布式搜索引擎 ElasticSearch 业界知名<br />
* 分布式链路追踪 Apache SkyWalking 链路追踪网格分析度量聚合可视化<br />
* 分布式日志中心 ELK 业界成熟解决方案<br />
* 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br />
</p>
<p><b>当前版本:</b> <span>v2.5.1</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
>更新日志</el-button
>
</p>
</el-col>
</el-row>
<el-divider />
<h2>AR巡检管理平台 Dashboard</h2>
</div>
</template>
<script setup name="Index" lang="ts">
const goTarget = (url: string) => {
window.open(url, '__blank');
};
</script>
<script setup name="Index" lang="ts"></script>
<style lang="scss" scoped>
.home {
blockquote {
padding: 10px 20px;
margin: 0 0 20px;
font-size: 17.5px;
border-left: 5px solid #eee;
}
hr {
margin-top: 20px;
margin-bottom: 20px;
border: 0;
border-top: 1px solid #eee;
}
.col-item {
margin-bottom: 20px;
}
ul {
padding: 0;
margin: 0;
}
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 13px;
color: #676a6c;
overflow-x: hidden;
ul {
list-style-type: none;
}
h4 {
margin-top: 0px;
}
h2 {
margin-top: 10px;
font-size: 26px;
font-weight: 100;
}
p {
margin-top: 10px;
b {
font-weight: 700;
}
}
.update-log {
ol {
display: block;
list-style-type: decimal;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
padding-inline-start: 40px;
}
}
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 24px;
font-weight: bold;
}
</style>

View File

@@ -107,7 +107,13 @@
</el-col>
<el-col :span="12">
<el-form-item label="区域" prop="regionId">
<el-select v-model="form.regionId" placeholder="请选择区域" filterable style="width: 100%">
<el-select
v-model="form.regionId"
placeholder="请选择任务模板后自动填充"
filterable
style="width: 100%"
:disabled="!!form.taskId && !form.id"
>
<el-option v-for="region in regionOptions" :key="region.id" :label="region.regionName" :value="region.id" />
</el-select>
</el-form-item>
@@ -121,7 +127,8 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- 仅编辑时显示状态字段 -->
<el-col v-if="form.id" :span="12">
<el-form-item label="执行状态" prop="status">
<el-select v-model="form.status" placeholder="请选择状态" style="width: 100%">
<el-option label="待执行" value="pending" />
@@ -134,34 +141,90 @@
</el-row>
<el-divider content-position="left">执行角色</el-divider>
<!-- 操作人和监护人 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="操作人" prop="operatorName">
<el-input v-model="form.operatorName" placeholder="请输入操作人姓名" />
<el-input
v-model="form.operatorName"
placeholder="请选择操作人"
readonly
style="width: 100%"
>
<template #append>
<el-button icon="User" @click="operatorSelectRef.open()" />
<el-button v-if="form.operatorName" icon="Close" @click="clearUser('operator')" />
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="监护人" prop="custodianName">
<el-input v-model="form.custodianName" placeholder="请输入监护人姓名" />
<el-input
v-model="form.custodianName"
placeholder="请选择监护人"
readonly
style="width: 100%"
>
<template #append>
<el-button icon="User" @click="custodianSelectRef.open()" />
<el-button v-if="form.custodianName" icon="Close" @click="clearUser('custodian')" />
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 送电人和受电人 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="送电人" prop="senderName">
<el-input v-model="form.senderName" placeholder="请输入送电人姓名" />
<el-input
v-model="form.senderName"
placeholder="请选择送电人"
readonly
style="width: 100%"
>
<template #append>
<el-button icon="User" @click="senderSelectRef.open()" />
<el-button v-if="form.senderName" icon="Close" @click="clearUser('sender')" />
</template>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="受电人" prop="recipientName">
<el-input v-model="form.recipientName" placeholder="请输入受电人姓名" />
<el-input
v-model="form.recipientName"
placeholder="请选择受电人"
readonly
style="width: 100%"
>
<template #append>
<el-button icon="User" @click="recipientSelectRef.open()" />
<el-button v-if="form.recipientName" icon="Close" @click="clearUser('recipient')" />
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 指挥人 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="指挥人" prop="commanderName">
<el-input v-model="form.commanderName" placeholder="请输入指挥人姓名" />
<el-input
v-model="form.commanderName"
placeholder="请选择指挥人"
readonly
style="width: 100%"
>
<template #append>
<el-button icon="User" @click="commanderSelectRef.open()" />
<el-button v-if="form.commanderName" icon="Close" @click="clearUser('commander')" />
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
@@ -173,18 +236,27 @@
</div>
</template>
</el-dialog>
<!-- 用户选择组件 -->
<UserSelect ref="operatorSelectRef" :multiple="false" @confirmCallBack="handleOperatorSelect" />
<UserSelect ref="custodianSelectRef" :multiple="false" @confirmCallBack="handleCustodianSelect" />
<UserSelect ref="senderSelectRef" :multiple="false" @confirmCallBack="handleSenderSelect" />
<UserSelect ref="recipientSelectRef" :multiple="false" @confirmCallBack="handleRecipientSelect" />
<UserSelect ref="commanderSelectRef" :multiple="false" @confirmCallBack="handleCommanderSelect" />
</div>
</template>
<script setup name="ArExecution" lang="ts">
import { listArExecution, getArExecution, delArExecution, addArExecution, updateArExecution } from '@/api/inspection/execution';
import { ArExecutionVO, ArExecutionQuery, ArExecutionForm } from '@/api/inspection/execution/types';
import { listArTask } from '@/api/inspection/task';
import { listArTask, getArTask } from '@/api/inspection/task';
import { ArTaskVO } from '@/api/inspection/task/types';
import { listArRegion } from '@/api/inspection/region';
import { ArRegionVO } from '@/api/inspection/region/types';
import { listArDevice } from '@/api/inspection/device';
import { ArDeviceVO } from '@/api/inspection/device/types';
import { UserVO } from '@/api/system/user/types';
import UserSelect from '@/components/UserSelect/index.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -203,6 +275,12 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const executionFormRef = ref<ElFormInstance>();
const operatorSelectRef = ref();
const custodianSelectRef = ref();
const senderSelectRef = ref();
const recipientSelectRef = ref();
const commanderSelectRef = ref();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
@@ -369,6 +447,90 @@ const handleExport = () => {
);
};
/** 监听任务模板变化,自动填充区域 */
watch(() => form.value.taskId, async (newTaskId) => {
if (newTaskId && !form.value.id) { // 仅新增时自动填充
try {
const res = await getArTask(newTaskId);
if (res.data && res.data.regionId) {
form.value.regionId = res.data.regionId;
const region = regionOptions.value.find(r => r.id === res.data.regionId);
if (region) {
proxy?.$modal.msgSuccess(`已自动填充区域:${region.regionName}`);
}
}
} catch (error) {
console.error('获取任务模板详情失败', error);
}
}
});
/** 操作人选择回调 */
const handleOperatorSelect = (users: UserVO[]) => {
if (users && users.length > 0) {
form.value.operatorId = users[0].userId;
form.value.operatorName = users[0].nickName;
}
};
/** 监护人选择回调 */
const handleCustodianSelect = (users: UserVO[]) => {
if (users && users.length > 0) {
form.value.custodianId = users[0].userId;
form.value.custodianName = users[0].nickName;
}
};
/** 送电人选择回调 */
const handleSenderSelect = (users: UserVO[]) => {
if (users && users.length > 0) {
form.value.senderId = users[0].userId;
form.value.senderName = users[0].nickName;
}
};
/** 受电人选择回调 */
const handleRecipientSelect = (users: UserVO[]) => {
if (users && users.length > 0) {
form.value.recipientId = users[0].userId;
form.value.recipientName = users[0].nickName;
}
};
/** 指挥人选择回调 */
const handleCommanderSelect = (users: UserVO[]) => {
if (users && users.length > 0) {
form.value.commanderId = users[0].userId;
form.value.commanderName = users[0].nickName;
}
};
/** 清空人员选择 */
const clearUser = (role: 'operator' | 'custodian' | 'sender' | 'recipient' | 'commander') => {
switch (role) {
case 'operator':
form.value.operatorId = undefined;
form.value.operatorName = undefined;
break;
case 'custodian':
form.value.custodianId = undefined;
form.value.custodianName = undefined;
break;
case 'sender':
form.value.senderId = undefined;
form.value.senderName = undefined;
break;
case 'recipient':
form.value.recipientId = undefined;
form.value.recipientName = undefined;
break;
case 'commander':
form.value.commanderId = undefined;
form.value.commanderName = undefined;
break;
}
};
onMounted(() => {
getOptions();
getList();

View File

@@ -44,7 +44,7 @@
</div>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">{{ proxy.$t('login.rememberPassword') }}</el-checkbox>
<el-form-item style="float: right">
<el-form-item v-if="false" style="float: right">
<el-button circle :title="proxy.$t('login.social.wechat')" @click="doSocialLogin('wechat')">
<svg-icon icon-class="wechat" />
</el-button>
@@ -73,7 +73,7 @@
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2025 疯狂的狮子Li All Rights Reserved.</span>
<span>Copyright © 2021-2025 NJCQ Tech All Rights Reserved.</span>
</div>
</div>
</template>
@@ -96,8 +96,8 @@ const { t } = useI18n();
const loginForm = ref<LoginData>({
tenantId: '000000',
username: 'admin',
password: 'admin123',
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
@@ -236,8 +236,9 @@ onMounted(() => {
justify-content: center;
align-items: center;
height: 100%;
background-image: url('../assets/images/login-background.jpg');
background-size: cover;
// background-image: url('../assets/images/login-background.jpg');
background-color: #28273a;
// background-size: cover;
}
.title-box {

View File

@@ -24,7 +24,7 @@ export default defineConfig(({ mode, command }) => {
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
target: 'http://localhost:8080',
target: 'http://119.45.199.65:8081',
changeOrigin: true,
ws: true,
rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')

View File

@@ -103,13 +103,14 @@ public class AuthController {
// 登录
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS);
// 注释掉登录欢迎消息推送 - UI定制需求
// Long userId = LoginHelper.getUserId();
// scheduledExecutorService.schedule(() -> {
// SseMessageDto dto = new SseMessageDto();
// dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
// dto.setUserIds(List.of(userId));
// SseMessageUtils.publishMessage(dto);
// }, 5, TimeUnit.SECONDS);
return R.ok(loginVo);
}

View File

@@ -4,7 +4,7 @@ spring.servlet.multipart.location: /ruoyi/server/temp
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
enabled: false
url: http://localhost:9090/admin
instance:
service-host-type: IP
@@ -16,7 +16,7 @@ spring.boot.admin.client:
--- # snail-job 配置
snail-job:
enabled: true
enabled: false
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
@@ -50,9 +50,9 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: root
url: jdbc:mysql://1Panel-mysql-TAXz:3306/ari?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: ari
password: fhWpsBYNxZfdAEph
# # 从库数据源
# slave:
# lazy: true
@@ -99,13 +99,13 @@ spring:
spring.data:
redis:
# 地址
host: localhost
host: 1Panel-redis-HCyW
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
database: 1
# redis 密码必须配置
password: ruoyi123
password: redis_X7Ehme
# 连接超时时间
timeout: 10s
# 是否开启ssl

View File

@@ -195,16 +195,16 @@ springdoc:
enabled: true
info:
# 标题
title: '标题RuoYi-Vue-Plus多租户管理系统_接口文档'
title: 'AR智能巡检平台-接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
description: 'AR智能巡检平台 接口文档'
# 版本
version: '版本号: ${project.version}'
# 作者信息
contact:
name: Lion Li
email: crazylionli@163.com
url: https://gitee.com/dromara/RuoYi-Vue-Plus
name: YANG JIANKUAN
email: tim.yee@hotmail.com
url: https://www.njcqit.com
#这里定义了两个分组,可定义多个,也可以不定义
group-configs:
- group: 1.演示模块

View File

@@ -1,6 +1,7 @@
package org.dromara.inspection.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
@@ -13,6 +14,8 @@ import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.vo.ArExecutionDetailVo;
import org.dromara.inspection.domain.vo.ArExecutionVo;
import org.dromara.inspection.service.IArExecutionService;
import lombok.RequiredArgsConstructor;
@@ -61,15 +64,16 @@ public class ArExecutionController extends BaseController {
}
/**
* 获取任务执行记录详细信息
* 获取任务执行记录详细信息(包含关联对象和步骤树)
*
* @param id 执行ID
*/
@SaCheckPermission("inspection:execution:query")
@GetMapping("/{id}")
public R<ArExecutionVo> getInfo(@NotNull(message = "执行ID不能为空")
@PathVariable("id") Long id) {
return R.ok(arExecutionService.queryById(id));
@SaIgnore
public R<ArExecutionDetailVo> getInfo(@NotNull(message = "执行ID不能为空")
@PathVariable("id") Long id) {
return R.ok(arExecutionService.queryDetailById(id));
}
/**
@@ -94,6 +98,18 @@ public class ArExecutionController extends BaseController {
return toAjax(arExecutionService.updateByBo(bo));
}
/**
* 批量提交任务执行结果
*/
@SaCheckPermission("inspection:execution:submit")
@Log(title = "提交任务执行结果", businessType = BusinessType.UPDATE)
@RepeatSubmit
@SaIgnore
@PostMapping("/submit")
public R<Void> submit(@Validated @RequestBody ArExecutionSubmitBo bo) {
return toAjax(arExecutionService.submitExecution(bo));
}
/**
* 删除任务执行记录
*

View File

@@ -40,9 +40,11 @@ public class ArStepRecordController extends BaseController {
private final IArStepRecordService arStepRecordService;
/**
/**
* 查询步骤执行记录列表
*/
@SaCheckPermission("inspection:stepRecord:list")
@GetMapping("/list")
public TableDataInfo<ArStepRecordVo> list(@Validated(QueryGroup.class) ArStepRecordBo bo, PageQuery pageQuery) {

View File

@@ -0,0 +1,51 @@
package org.dromara.inspection.domain.bo;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 任务执行提交业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArExecutionSubmitBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执行ID
*/
@NotNull(message = "执行ID不能为空")
private Long id;
/**
* 状态(pending/in_progress/completed/cancelled)
*/
private String status;
/**
* 开始时间
*/
private Date startTime;
/**
* 结束时间
*/
private Date endTime;
/**
* 步骤执行树
*/
@Valid
private List<ArStepRecordTreeBo> stepTree;
}

View File

@@ -0,0 +1,89 @@
package org.dromara.inspection.domain.bo;
import jakarta.validation.Valid;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 步骤执行记录树形业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArStepRecordTreeBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 步骤ID仅用于引用
*/
private Long id;
/**
* 执行记录ID用于更新现有记录
*/
private Long recordId;
/**
* 执行状态(pending/completed/skipped)
*/
private String recordStatus;
/**
* 是否完成(0否 1是)
*/
private String isDone;
/**
* 开始时间
*/
private Date startTime;
/**
* 完成时间
*/
private Date completionTime;
/**
* 耗时(秒)
*/
private Integer duration;
/**
* 文本反馈
*/
private String textFeedback;
/**
* 语音识别文本
*/
private String voiceText;
/**
* AI识别结果(JSON)
*/
private String aiResult;
/**
* 执行人ID
*/
private Long executorId;
/**
* 执行人姓名
*/
private String executorName;
/**
* 子步骤列表
*/
@Valid
private List<ArStepRecordTreeBo> children;
}

View File

@@ -0,0 +1,46 @@
package org.dromara.inspection.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 任务执行详情视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArExecutionDetailVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 执行基本信息
*/
private ArExecutionVo execution;
/**
* 任务模板信息
*/
private ArTaskVo task;
/**
* 区域信息
*/
private ArRegionVo region;
/**
* 设备信息
*/
private ArDeviceVo device;
/**
* 步骤执行树
*/
private List<ArStepRecordTreeVo> stepTree;
}

View File

@@ -0,0 +1,204 @@
package org.dromara.inspection.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 步骤执行记录树形视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class ArStepRecordTreeVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
// ========== 步骤模板信息(来自 ar_step ==========
/**
* 步骤ID
*/
private Long id;
/**
* 所属任务ID
*/
private Long taskId;
/**
* 父步骤ID(0为顶级)
*/
private Long parentId;
/**
* 祖级列表
*/
private String ancestors;
/**
* 步骤名称
*/
private String stepName;
/**
* 步骤内容描述
*/
private String stepContent;
/**
* 步骤语音文本
*/
private String contentVoice;
/**
* 排序号
*/
private Integer orderNum;
/**
* 关联点位ID
*/
private Long pointId;
/**
* 需要语音朗读(0否 1是)
*/
private String needVoiceRead;
/**
* 需要用户复述(0否 1是)
*/
private String needVoiceRephrase;
/**
* 复述提示文本
*/
private String rephraseContent;
/**
* 复述语音文本
*/
private String rephraseVoice;
/**
* 需要确认(0否 1是)
*/
private String needVoiceConfirm;
/**
* 确认提示文本
*/
private String confirmContent;
/**
* 确认语音文本
*/
private String confirmVoice;
/**
* 确认词
*/
private String confirmWord;
/**
* 需要AI识别(0否 1是)
*/
private String needAi;
/**
* AI目标名称
*/
private String aiTargetName;
/**
* AI配置数据(预留)
*/
private Map<String, Object> aiData;
/**
* 是否操作步骤(0否 1是)
*/
private String isOperation;
/**
* 是否叶子节点(0否 1是)
*/
private String isLeaf;
/**
* 创建时间
*/
private Date createTime;
// ========== 执行记录信息(来自 ar_step_record ==========
/**
* 执行记录ID
*/
private Long recordId;
/**
* 执行状态(pending/completed/skipped)
*/
private String recordStatus;
/**
* 是否完成(0否 1是)
*/
private String isDone;
/**
* 开始时间
*/
private Date startTime;
/**
* 完成时间
*/
private Date completionTime;
/**
* 耗时(秒)
*/
private Integer duration;
/**
* 文本反馈
*/
private String textFeedback;
/**
* 语音识别文本
*/
private String voiceText;
/**
* AI识别结果(JSON)
*/
private String aiResult;
/**
* 执行人ID
*/
private Long executorId;
/**
* 执行人姓名
*/
private String executorName;
// ========== 树形结构 ==========
/**
* 子步骤列表
*/
private List<ArStepRecordTreeVo> children;
}

View File

@@ -3,6 +3,8 @@ package org.dromara.inspection.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.vo.ArExecutionDetailVo;
import org.dromara.inspection.domain.vo.ArExecutionVo;
import java.util.Collection;
@@ -23,6 +25,22 @@ public interface IArExecutionService {
*/
ArExecutionVo queryById(Long id);
/**
* 查询任务执行详情(包含关联对象和步骤树)
*
* @param id 执行ID
* @return 任务执行详情VO
*/
ArExecutionDetailVo queryDetailById(Long id);
/**
* 批量提交任务执行结果
*
* @param bo 提交业务对象
* @return 是否成功
*/
Boolean submitExecution(ArExecutionSubmitBo bo);
/**
* 查询列表
*/

View File

@@ -10,16 +10,21 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.inspection.domain.ArExecution;
import org.dromara.inspection.domain.ArStep;
import org.dromara.inspection.domain.ArStepRecord;
import org.dromara.inspection.domain.bo.ArExecutionBo;
import org.dromara.inspection.domain.vo.ArExecutionVo;
import org.dromara.inspection.mapper.ArExecutionMapper;
import org.dromara.inspection.domain.bo.ArExecutionSubmitBo;
import org.dromara.inspection.domain.bo.ArStepRecordTreeBo;
import org.dromara.inspection.domain.vo.*;
import org.dromara.inspection.mapper.*;
import org.dromara.inspection.service.IArExecutionService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.AllArgsConstructor;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
* 任务执行记录Service业务层处理
@@ -32,6 +37,11 @@ import java.util.Map;
public class ArExecutionServiceImpl implements IArExecutionService {
private final ArExecutionMapper baseMapper;
private final ArTaskMapper taskMapper;
private final ArRegionMapper regionMapper;
private final ArDeviceMapper deviceMapper;
private final ArStepMapper stepMapper;
private final ArStepRecordMapper stepRecordMapper;
@Override
public ArExecutionVo queryById(Long id) {
@@ -141,4 +151,253 @@ public class ArExecutionServiceImpl implements IArExecutionService {
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public ArExecutionDetailVo queryDetailById(Long id) {
// 1. 查询执行记录
ArExecutionVo execution = baseMapper.selectVoById(id);
if (execution == null) {
throw new ServiceException("任务执行记录不存在");
}
// 2. 查询关联对象
ArTaskVo task = execution.getTaskId() != null
? taskMapper.selectVoById(execution.getTaskId()) : null;
ArRegionVo region = execution.getRegionId() != null
? regionMapper.selectVoById(execution.getRegionId()) : null;
ArDeviceVo device = execution.getDeviceId() != null
? deviceMapper.selectVoById(execution.getDeviceId()) : null;
// 3. 查询步骤模板
List<ArStepVo> allSteps = new ArrayList<>();
if (execution.getTaskId() != null) {
LambdaQueryWrapper<ArStep> stepWrapper = Wrappers.lambdaQuery();
stepWrapper.eq(ArStep::getTaskId, execution.getTaskId());
stepWrapper.orderByAsc(ArStep::getOrderNum);
allSteps = stepMapper.selectVoList(stepWrapper);
}
// 4. 查询执行记录
LambdaQueryWrapper<ArStepRecord> recordWrapper = Wrappers.lambdaQuery();
recordWrapper.eq(ArStepRecord::getExecutionId, id);
List<ArStepRecordVo> allRecords = stepRecordMapper.selectVoList(recordWrapper);
// 5. 构建步骤记录 Map
Map<Long, ArStepRecordVo> recordMap = allRecords.stream()
.collect(Collectors.toMap(ArStepRecordVo::getStepId, r -> r, (r1, r2) -> r1));
// 6. 构建步骤树
List<ArStepRecordTreeVo> stepTree = buildStepRecordTree(allSteps, recordMap, 0L);
// 7. 组装返回对象
ArExecutionDetailVo detail = new ArExecutionDetailVo();
detail.setExecution(execution);
detail.setTask(task);
detail.setRegion(region);
detail.setDevice(device);
detail.setStepTree(stepTree);
return detail;
}
/**
* 递归构建步骤执行记录树
*
* @param allSteps 所有步骤模板
* @param recordMap 执行记录Map (stepId -> record)
* @param parentId 父步骤ID
* @return 树形步骤执行记录列表
*/
private List<ArStepRecordTreeVo> buildStepRecordTree(
List<ArStepVo> allSteps,
Map<Long, ArStepRecordVo> recordMap,
Long parentId) {
List<ArStepRecordTreeVo> tree = new ArrayList<>();
for (ArStepVo step : allSteps) {
if (step.getParentId().equals(parentId)) {
ArStepRecordTreeVo treeNode = new ArStepRecordTreeVo();
// 复制步骤模板信息
BeanUtils.copyProperties(step, treeNode);
// 合并执行记录信息
ArStepRecordVo record = recordMap.get(step.getId());
if (record != null) {
treeNode.setRecordId(record.getId());
treeNode.setRecordStatus(record.getStatus());
treeNode.setIsDone(record.getIsDone());
treeNode.setStartTime(record.getStartTime());
treeNode.setCompletionTime(record.getCompletionTime());
treeNode.setDuration(record.getDuration());
treeNode.setTextFeedback(record.getTextFeedback());
treeNode.setVoiceText(record.getVoiceText());
treeNode.setAiResult(record.getAiResult());
treeNode.setExecutorId(record.getExecutorId());
treeNode.setExecutorName(record.getExecutorName());
}
// 递归查找子节点
List<ArStepRecordTreeVo> children = buildStepRecordTree(allSteps, recordMap, step.getId());
if (!children.isEmpty()) {
treeNode.setChildren(children);
}
tree.add(treeNode);
}
}
return tree;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean submitExecution(ArExecutionSubmitBo bo) {
// 1. 校验执行记录是否存在
ArExecution execution = baseMapper.selectById(bo.getId());
if (execution == null) {
throw new ServiceException("任务执行记录不存在");
}
// 2. 收集所有需要更新的步骤记录
List<ArStepRecord> recordsToUpdate = new ArrayList<>();
if (bo.getStepTree() != null && !bo.getStepTree().isEmpty()) {
collectRecordsToUpdate(bo.getStepTree(), bo.getId(), recordsToUpdate);
}
// 3. 批量更新步骤记录
if (!recordsToUpdate.isEmpty()) {
boolean updateSuccess = stepRecordMapper.updateBatchById(recordsToUpdate);
if (!updateSuccess) {
throw new ServiceException("步骤记录更新失败");
}
}
// 4. 计算总步骤数和已完成步骤数
StepCountResult countResult = countSteps(bo.getStepTree());
// 5. 更新执行记录
ArExecution updateExecution = new ArExecution();
updateExecution.setId(bo.getId());
// 设置状态
if (StringUtils.isNotBlank(bo.getStatus())) {
updateExecution.setStatus(bo.getStatus());
}
// 设置时间
if (bo.getStartTime() != null) {
updateExecution.setStartTime(bo.getStartTime());
}
if (bo.getEndTime() != null) {
updateExecution.setEndTime(bo.getEndTime());
}
// 自动设置开始时间
if ("in_progress".equals(updateExecution.getStatus()) && execution.getStartTime() == null) {
updateExecution.setStartTime(new Date());
}
// 自动设置结束时间
if (("completed".equals(updateExecution.getStatus()) || "cancelled".equals(updateExecution.getStatus()))
&& execution.getEndTime() == null) {
updateExecution.setEndTime(new Date());
}
// 设置步骤统计
updateExecution.setTotalSteps(countResult.getTotal());
updateExecution.setCompletedSteps(countResult.getCompleted());
return baseMapper.updateById(updateExecution) > 0;
}
/**
* 递归收集所有需要更新的步骤记录
*
* @param stepTree 步骤树
* @param executionId 执行ID
* @param result 结果列表
*/
private void collectRecordsToUpdate(List<ArStepRecordTreeBo> stepTree, Long executionId, List<ArStepRecord> result) {
if (stepTree == null || stepTree.isEmpty()) {
return;
}
for (ArStepRecordTreeBo stepBo : stepTree) {
// 只更新有 recordId 的记录
if (stepBo.getRecordId() != null) {
ArStepRecord record = new ArStepRecord();
record.setId(stepBo.getRecordId());
record.setExecutionId(executionId);
record.setStepId(stepBo.getId());
record.setStatus(stepBo.getRecordStatus());
record.setIsDone(stepBo.getIsDone());
record.setStartTime(stepBo.getStartTime());
record.setCompletionTime(stepBo.getCompletionTime());
record.setTextFeedback(stepBo.getTextFeedback());
record.setVoiceText(stepBo.getVoiceText());
record.setAiResult(stepBo.getAiResult());
record.setExecutorId(stepBo.getExecutorId());
record.setExecutorName(stepBo.getExecutorName());
// 自动计算耗时
if (stepBo.getStartTime() != null && stepBo.getCompletionTime() != null) {
long durationMillis = stepBo.getCompletionTime().getTime() - stepBo.getStartTime().getTime();
record.setDuration((int) (durationMillis / 1000));
} else if (stepBo.getDuration() != null) {
record.setDuration(stepBo.getDuration());
}
result.add(record);
}
// 递归处理子步骤
if (stepBo.getChildren() != null && !stepBo.getChildren().isEmpty()) {
collectRecordsToUpdate(stepBo.getChildren(), executionId, result);
}
}
}
/**
* 递归统计步骤数
*
* @param stepTree 步骤树
* @return 统计结果
*/
private StepCountResult countSteps(List<ArStepRecordTreeBo> stepTree) {
int totalCount = 0;
int completedCount = 0;
if (stepTree == null || stepTree.isEmpty()) {
return new StepCountResult(totalCount, completedCount);
}
for (ArStepRecordTreeBo stepBo : stepTree) {
// 只统计有 recordId 的步骤
if (stepBo.getRecordId() != null) {
totalCount++;
if ("1".equals(stepBo.getIsDone()) || "completed".equals(stepBo.getRecordStatus())) {
completedCount++;
}
}
// 递归统计子步骤
if (stepBo.getChildren() != null && !stepBo.getChildren().isEmpty()) {
StepCountResult childCounts = countSteps(stepBo.getChildren());
totalCount += childCounts.getTotal();
completedCount += childCounts.getCompleted();
}
}
return new StepCountResult(totalCount, completedCount);
}
/**
* 步骤统计结果内部类
*/
@lombok.Data
@AllArgsConstructor
private static class StepCountResult {
private int total;
private int completed;
}
}

View File

@@ -0,0 +1,366 @@
# AR智能巡检平台 - 数据库安装指南
## 文件说明
本目录包含以下 SQL 文件:
### 数据库表结构文件
- **ar-inspection-tables.sql** - AR巡检模块数据库表结构(8个业务表)
- **ar-inspection-tables-rollback.sql** - 数据库表回滚脚本(删除所有巡检表)
### 菜单权限文件
- **ar-inspection-menu.sql** - 菜单和权限初始化SQL(49条权限配置)
- **ar-inspection-menu-rollback.sql** - 菜单权限回滚SQL
## 数据库表清单
| 序号 | 表名 | 说明 | 用途 |
|------|------|------|------|
| 1 | ar_device | AR设备管理表 | 管理AR眼镜等设备信息 |
| 2 | ar_region | 巡检区域管理表 | 管理巡检区域划分 |
| 3 | ar_point | 巡检点位管理表 | 管理具体巡检点位 |
| 4 | ar_task | 巡检任务模板表 | 定义巡检任务模板 |
| 5 | ar_step | 巡检步骤表 | 定义任务的执行步骤(支持树形结构) |
| 6 | ar_step_media | 步骤媒体文件表 | 存储步骤相关的图片/视频/音频 |
| 7 | ar_execution | 任务执行记录表 | 记录任务的执行情况 |
| 8 | ar_step_record | 步骤执行记录表 | 记录每个步骤的执行详情 |
## 表关系图
```
ar_device (设备)
└─> ar_execution (执行记录)
ar_region (区域)
├─> ar_point (点位)
├─> ar_task (任务模板)
└─> ar_execution (执行记录)
ar_point (点位)
└─> ar_step (巡检步骤)
ar_task (任务模板)
├─> ar_step (巡检步骤)
└─> ar_execution (执行记录)
ar_step (巡检步骤)
├─> ar_step (父子关系-树形结构)
└─> ar_step_record (步骤记录)
ar_execution (执行记录)
└─> ar_step_record (步骤记录)
ar_step_record (步骤记录)
└─> ar_step_media (步骤媒体)
```
## 安装步骤
### 第一步: 创建数据库表
根据你的环境选择以下任一方式执行:
#### 方式1: 命令行导入
```bash
# 进入MySQL
mysql -u root -p
# 选择数据库(请根据实际情况修改数据库名)
use ry-vue;
# 导入表结构
source /path/to/ar-inspection/script/sql/ar-inspection-tables.sql;
# 验证表是否创建成功
show tables like 'ar_%';
# 查看表结构(以ar_device为例)
desc ar_device;
# 退出MySQL
exit;
```
#### 方式2: 直接执行命令
```bash
# 一条命令完成导入
mysql -u root -p -D ry-vue < /path/to/ar-inspection/script/sql/ar-inspection-tables.sql
```
#### 方式3: 使用 Navicat/DBeaver/DataGrip 等 GUI 工具
1. 打开工具并连接到数据库
2. 选择数据库 `ry-vue`
3. 打开 SQL 文件: `ar-inspection-tables.sql`
4. 点击"运行"或"执行"按钮
5. 验证表是否创建成功
### 第二步: 导入菜单权限数据
```bash
# 方式1: 命令行
mysql -u root -p -D ry-vue < /path/to/ar-inspection/script/sql/ar-inspection-menu.sql
# 方式2: MySQL客户端
mysql -u root -p
use ry-vue;
source /path/to/ar-inspection/script/sql/ar-inspection-menu.sql;
# 验证菜单是否导入成功
SELECT menu_id, menu_name, parent_id, menu_type, perms
FROM sys_menu
WHERE menu_id >= 2000 AND menu_id < 2200
ORDER BY menu_id;
```
### 第三步: 分配权限给角色
1. 登录系统管理后台 (http://localhost:8080 或你的实际地址)
2. 进入 **系统管理 > 角色管理**
3. 选择需要授权的角色(如"管理员"),点击"修改"
4. 在菜单权限中勾选 **"AR巡检"** 及其子菜单
5. 点击"确定"保存设置
### 第四步: 验证安装
```bash
# 验证所有表是否创建成功
mysql -u root -p -D ry-vue -e "SHOW TABLES LIKE 'ar_%';"
# 验证菜单权限是否导入成功
mysql -u root -p -D ry-vue -e "SELECT COUNT(*) as menu_count FROM sys_menu WHERE menu_id >= 2000 AND menu_id < 2200;"
# 应该返回 49 条记录
# 验证表结构(查看ar_device表为例)
mysql -u root -p -D ry-vue -e "DESC ar_device;"
```
## 回滚操作
如果需要删除所有巡检模块的表和菜单权限,按以下顺序执行:
### 删除菜单权限
```bash
mysql -u root -p -D ry-vue < /path/to/ar-inspection/script/sql/ar-inspection-menu-rollback.sql
```
### 删除数据库表
```bash
mysql -u root -p -D ry-vue < /path/to/ar-inspection/script/sql/ar-inspection-tables-rollback.sql
```
**警告**: 回滚操作会永久删除所有表及其数据,请在执行前确保已备份重要数据!
## 技术特性
### 1. 多租户支持
- 所有表包含 `tenant_id` 字段
- 默认租户ID为 `000000`
- 支持数据隔离,不同租户数据互不干扰
### 2. 逻辑删除
- 所有表包含 `del_flag` 字段
- `0` = 存在, `1` = 已删除
- 删除操作不会物理删除数据,仅标记为已删除
### 3. 审计字段
所有表包含以下审计字段:
- `create_dept` - 创建部门
- `create_by` - 创建者
- `create_time` - 创建时间
- `update_by` - 更新者
- `update_time` - 更新时间
### 4. 主键策略
- 使用雪花ID算法(ASSIGN_ID)
- 由应用程序生成,不使用数据库自增
- 64位长整型,分布式友好
### 5. JSON字段支持
以下表包含JSON格式字段:
- `ar_region.region_data` - 区域业务数据
- `ar_point.position_data` - 点位坐标等数据
- `ar_step.ai_data` - AI配置数据
- `ar_step_record.ai_result` - AI识别结果
JSON字段由 MyBatis-Plus 的 `JacksonTypeHandler` 自动处理。
### 6. 树形结构支持
`ar_step` 表支持树形结构:
- `parent_id` - 父步骤ID(0为顶级)
- `ancestors` - 祖级列表(如: 0,1,2)
- `is_leaf` - 是否叶子节点
### 7. 索引优化
已根据常见查询场景配置索引:
- 主键索引 (PRIMARY KEY)
- 唯一索引 (UNIQUE KEY) - 防止重复数据
- 普通索引 (KEY) - 加速查询
## 数据字典
### 设备状态 (ar_device.status)
- `0` - 启用
- `1` - 停用
### 区域状态 (ar_region.status)
- `0` - 正常
- `1` - 停用
### 任务状态 (ar_task.status)
- `0` - 正常
- `1` - 停用
### 执行状态 (ar_execution.status)
- `pending` - 待执行
- `in_progress` - 执行中
- `completed` - 已完成
- `cancelled` - 已取消
### 步骤记录状态 (ar_step_record.status)
- `pending` - 待执行
- `completed` - 已完成
- `skipped` - 已跳过
### 媒体类型 (ar_step_media.media_type)
- `image` - 图片
- `video` - 视频
- `audio` - 音频
### Yes/No 标志
- `0` - 否/No
- `1` - 是/Yes
适用于以下字段:
- `need_voice_read` - 需要语音朗读
- `need_voice_rephrase` - 需要用户复述
- `need_voice_confirm` - 需要确认
- `need_ai` - 需要AI识别
- `is_operation` - 是否操作步骤
- `is_leaf` - 是否叶子节点
- `is_done` - 是否完成
## 注意事项
### 1. 数据库版本要求
- **MySQL 8.0+** (推荐 8.0.28 或更高版本)
- 字符集: `utf8mb4`
- 排序规则: `utf8mb4_unicode_ci`
### 2. 执行顺序
必须按以下顺序执行:
1. 先创建表结构 (`ar-inspection-tables.sql`)
2. 再导入菜单权限 (`ar-inspection-menu.sql`)
回滚时按相反顺序:
1. 先删除菜单权限 (`ar-inspection-menu-rollback.sql`)
2. 再删除表结构 (`ar-inspection-tables-rollback.sql`)
### 3. 数据库名称
默认数据库名为 `ry-vue`,如果你的数据库名称不同,请在执行前修改:
```bash
# 使用你的实际数据库名
use your_database_name;
```
### 4. 权限要求
执行SQL的数据库用户需要以下权限:
- CREATE - 创建表
- DROP - 删除表
- INSERT - 插入数据(菜单权限)
- ALTER - 修改表结构
### 5. 备份建议
在生产环境执行前,强烈建议:
1. 完整备份现有数据库
2. 先在测试环境验证
3. 选择低峰期执行
备份命令:
```bash
# 备份整个数据库
mysqldump -u root -p ry-vue > backup_$(date +%Y%m%d_%H%M%S).sql
# 仅备份巡检模块表
mysqldump -u root -p ry-vue ar_device ar_region ar_point ar_task ar_step ar_step_media ar_execution ar_step_record > ar_inspection_backup_$(date +%Y%m%d_%H%M%S).sql
```
### 6. 索引维护
大数据量情况下建议定期优化索引:
```sql
-- 分析表
ANALYZE TABLE ar_device, ar_region, ar_point, ar_task, ar_step, ar_step_media, ar_execution, ar_step_record;
-- 优化表
OPTIMIZE TABLE ar_device, ar_region, ar_point, ar_task, ar_step, ar_step_media, ar_execution, ar_step_record;
```
## 示例数据
SQL文件中已包含示例数据(默认注释),如需使用可取消注释:
```sql
-- 设备示例数据
INSERT INTO ar_device VALUES(1, '000000', 'AR眼镜-001', 'DEV001', 'HoloLens 2', '0', 'AR智能眼镜设备', '0', 103, 1, sysdate(), null, null);
-- 区域示例数据
INSERT INTO ar_region VALUES(1, '000000', '主控室', 'REGION001', null, '0', '电厂主控室区域', '0', 103, 1, sysdate(), null, null);
-- 点位示例数据
INSERT INTO ar_point VALUES(1, '000000', 1, '主控台', 'POINT001', null, '主控台点位', '0', 103, 1, sysdate(), null, null);
-- 任务模板示例数据
INSERT INTO ar_task VALUES(1, '000000', '日常巡检任务', 'TASK001', 1, 'daily', '0', '每日例行巡检任务', '0', 103, 1, sysdate(), null, null);
```
## 故障排查
### 问题1: 表已存在错误
```
ERROR 1050 (42S01): Table 'ar_device' already exists
```
**解决方案**:
1. 先执行回滚脚本删除旧表
2. 或手动删除冲突的表
3. 再重新执行创建脚本
### 问题2: 权限不足错误
```
ERROR 1142 (42000): CREATE command denied to user
```
**解决方案**: 使用具有足够权限的用户(如root)执行,或联系数据库管理员授予权限。
### 问题3: 菜单ID冲突
```
ERROR 1062 (23000): Duplicate entry '2000' for key 'PRIMARY'
```
**解决方案**:
1. 先执行菜单回滚脚本
2. 或手动删除冲突的菜单
3. 再重新执行菜单导入脚本
### 问题4: 字符集不匹配
```
ERROR 1115 (42000): Unknown character set: 'utf8mb4'
```
**解决方案**: 升级MySQL到5.5.3或更高版本,或修改SQL文件中的字符集为utf8。
## 技术支持
如有问题,请通过以下方式获取帮助:
1. 查看项目文档: `CLAUDE.md``AR-INSPECTION-SETUP.md`
2. 提交 Issue 到项目仓库
3. 联系开发团队
## 更新日志
- **2025-01-13**: 初始版本,创建8个业务表和49条权限配置

View File

@@ -0,0 +1,43 @@
-- ----------------------------
-- AR智能巡检平台 - 数据库表回滚SQL
-- 用于删除所有巡检模块相关表
-- 创建日期: 2025-01-13
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- 删除所有AR巡检模块表
-- 注意: 此操作会永久删除表及其数据,请谨慎执行!
-- ----------------------------
DROP TABLE IF EXISTS `ar_step_media`;
DROP TABLE IF EXISTS `ar_step_record`;
DROP TABLE IF EXISTS `ar_execution`;
DROP TABLE IF EXISTS `ar_step`;
DROP TABLE IF EXISTS `ar_task`;
DROP TABLE IF EXISTS `ar_point`;
DROP TABLE IF EXISTS `ar_region`;
DROP TABLE IF EXISTS `ar_device`;
SET FOREIGN_KEY_CHECKS = 1;
-- ----------------------------
-- 回滚完成
-- ----------------------------
-- 已删除以下8个表:
-- 1. ar_device - AR设备管理表
-- 2. ar_region - 巡检区域管理表
-- 3. ar_point - 巡检点位管理表
-- 4. ar_task - 巡检任务模板表
-- 5. ar_step - 巡检步骤表
-- 6. ar_step_media - 步骤媒体文件表
-- 7. ar_execution - 任务执行记录表
-- 8. ar_step_record - 步骤执行记录表
-- ----------------------------
-- 执行方式:
-- mysql -u root -p -D ry-vue < /path/to/ar-inspection-tables-rollback.sql
-- 或
-- mysql> use ry-vue;
-- mysql> source /path/to/ar-inspection-tables-rollback.sql;

View File

@@ -0,0 +1,334 @@
-- ----------------------------
-- AR智能巡检平台 - 数据库表结构SQL
-- 适用于 MySQL 8.0+
-- 创建日期: 2025-01-13
-- ----------------------------
-- 设置字符集和排序规则
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- 表1: AR设备管理表
-- ----------------------------
DROP TABLE IF EXISTS `ar_device`;
CREATE TABLE `ar_device` (
`id` bigint(20) NOT NULL COMMENT '设备ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`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 '备注',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_device_no` (`device_no`, `tenant_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='AR设备管理表';
-- ----------------------------
-- 表2: 巡检区域管理表
-- ----------------------------
DROP TABLE IF EXISTS `ar_region`;
CREATE TABLE `ar_region` (
`id` bigint(20) NOT NULL COMMENT '区域ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`region_name` varchar(100) NOT NULL COMMENT '区域名称',
`region_code` varchar(50) NOT NULL COMMENT '区域编码',
`region_data` text DEFAULT NULL COMMENT '区域业务数据(JSON格式,预留)',
`status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_region_code` (`region_code`, `tenant_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检区域管理表';
-- ----------------------------
-- 表3: 巡检点位管理表
-- ----------------------------
DROP TABLE IF EXISTS `ar_point`;
CREATE TABLE `ar_point` (
`id` bigint(20) NOT NULL COMMENT '点位ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`region_id` bigint(20) NOT NULL COMMENT '所属区域ID',
`point_name` varchar(100) NOT NULL COMMENT '点位名称',
`point_code` varchar(50) NOT NULL COMMENT '点位编码',
`position_data` text DEFAULT NULL COMMENT '点位业务数据(JSON格式,包含坐标、预设等)',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_point_code` (`point_code`, `tenant_id`),
KEY `idx_region_id` (`region_id`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检点位管理表';
-- ----------------------------
-- 表4: 巡检任务模板表
-- ----------------------------
DROP TABLE IF EXISTS `ar_task`;
CREATE TABLE `ar_task` (
`id` bigint(20) NOT NULL COMMENT '任务ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`task_name` varchar(100) NOT NULL COMMENT '任务名称',
`task_code` varchar(50) NOT NULL COMMENT '任务编码',
`region_id` bigint(20) DEFAULT NULL COMMENT '关联区域ID',
`task_type` varchar(50) DEFAULT NULL COMMENT '任务类型',
`status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_task_code` (`task_code`, `tenant_id`),
KEY `idx_region_id` (`region_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_task_type` (`task_type`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检任务模板表';
-- ----------------------------
-- 表5: 巡检步骤表(支持树形结构)
-- ----------------------------
DROP TABLE IF EXISTS `ar_step`;
CREATE TABLE `ar_step` (
`id` bigint(20) NOT NULL COMMENT '步骤ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`task_id` bigint(20) NOT NULL COMMENT '所属任务ID',
`parent_id` bigint(20) DEFAULT 0 COMMENT '父步骤ID(0为顶级)',
`ancestors` varchar(500) DEFAULT '' COMMENT '祖级列表',
`step_name` varchar(100) NOT NULL COMMENT '步骤名称',
`step_content` text DEFAULT NULL COMMENT '步骤内容描述',
`content_voice` varchar(500) DEFAULT NULL COMMENT '步骤语音文本',
`order_num` int(11) DEFAULT 0 COMMENT '排序号',
`point_id` bigint(20) 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 DEFAULT NULL COMMENT '复述提示文本',
`rephrase_voice` varchar(500) DEFAULT NULL COMMENT '复述语音文本',
`need_voice_confirm` char(1) DEFAULT '0' COMMENT '需要确认(0否 1是)',
`confirm_content` text DEFAULT NULL COMMENT '确认提示文本',
`confirm_voice` varchar(500) DEFAULT NULL COMMENT '确认语音文本',
`confirm_word` varchar(100) DEFAULT NULL COMMENT '确认词',
`need_ai` char(1) DEFAULT '0' COMMENT '需要AI识别(0否 1是)',
`ai_target_name` varchar(100) DEFAULT NULL COMMENT 'AI目标名称',
`ai_data` text DEFAULT NULL COMMENT 'AI配置数据(JSON格式,预留)',
`is_operation` char(1) DEFAULT '0' COMMENT '是否操作步骤(0否 1是)',
`is_leaf` char(1) DEFAULT '1' COMMENT '是否叶子节点(0否 1是)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_task_id` (`task_id`),
KEY `idx_parent_id` (`parent_id`),
KEY `idx_point_id` (`point_id`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_order_num` (`order_num`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='巡检步骤表(支持树形结构)';
-- ----------------------------
-- 表6: 步骤媒体文件表
-- ----------------------------
DROP TABLE IF EXISTS `ar_step_media`;
CREATE TABLE `ar_step_media` (
`id` bigint(20) NOT NULL COMMENT '媒体ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`step_record_id` bigint(20) NOT NULL COMMENT '步骤记录ID',
`media_type` varchar(50) NOT NULL COMMENT '媒体类型(image/video/audio)',
`file_url` varchar(500) NOT NULL COMMENT '文件URL',
`file_name` varchar(200) DEFAULT NULL COMMENT '文件名称',
`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)',
`upload_time` datetime DEFAULT NULL COMMENT '上传时间',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_step_record_id` (`step_record_id`),
KEY `idx_media_type` (`media_type`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='步骤媒体文件表';
-- ----------------------------
-- 表7: 任务执行记录表
-- ----------------------------
DROP TABLE IF EXISTS `ar_execution`;
CREATE TABLE `ar_execution` (
`id` bigint(20) NOT NULL COMMENT '执行ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`task_id` bigint(20) NOT NULL COMMENT '任务模板ID',
`execution_code` varchar(50) NOT NULL COMMENT '执行编号',
`region_id` bigint(20) DEFAULT NULL COMMENT '区域ID',
`device_id` bigint(20) DEFAULT NULL COMMENT '使用设备ID',
`operator_id` bigint(20) DEFAULT NULL COMMENT '操作人ID',
`operator_name` varchar(50) DEFAULT NULL COMMENT '操作人姓名',
`custodian_id` bigint(20) DEFAULT NULL COMMENT '监护人ID',
`custodian_name` varchar(50) DEFAULT NULL COMMENT '监护人姓名',
`sender_id` bigint(20) DEFAULT NULL COMMENT '发令人ID',
`sender_name` varchar(50) DEFAULT NULL COMMENT '发令人姓名',
`recipient_id` bigint(20) DEFAULT NULL COMMENT '收令人ID',
`recipient_name` varchar(50) DEFAULT NULL COMMENT '收令人姓名',
`commander_id` bigint(20) 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(11) DEFAULT 0 COMMENT '总步骤数',
`completed_steps` int(11) DEFAULT 0 COMMENT '已完成步骤数',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_execution_code` (`execution_code`, `tenant_id`),
KEY `idx_task_id` (`task_id`),
KEY `idx_region_id` (`region_id`),
KEY `idx_device_id` (`device_id`),
KEY `idx_status` (`status`),
KEY `idx_tenant_id` (`tenant_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='任务执行记录表';
-- ----------------------------
-- 表8: 步骤执行记录表
-- ----------------------------
DROP TABLE IF EXISTS `ar_step_record`;
CREATE TABLE `ar_step_record` (
`id` bigint(20) NOT NULL COMMENT '记录ID',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`execution_id` bigint(20) NOT NULL COMMENT '任务执行ID',
`step_id` bigint(20) 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(11) DEFAULT 0 COMMENT '耗时(秒)',
`text_feedback` text DEFAULT NULL COMMENT '文本反馈',
`voice_text` text DEFAULT NULL COMMENT '语音识别文本',
`ai_result` text DEFAULT NULL COMMENT 'AI识别结果(JSON格式)',
`executor_id` bigint(20) DEFAULT NULL COMMENT '执行人ID',
`executor_name` varchar(50) DEFAULT NULL COMMENT '执行人姓名',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0存在 1删除)',
`create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_execution_id` (`execution_id`),
KEY `idx_step_id` (`step_id`),
KEY `idx_status` (`status`),
KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='步骤执行记录表';
-- ----------------------------
-- 插入示例数据(可选)
-- ----------------------------
-- 示例设备数据
-- INSERT INTO ar_device VALUES(1, '000000', 'AR眼镜-001', 'DEV001', 'HoloLens 2', '0', 'AR智能眼镜设备', '0', 103, 1, sysdate(), null, null);
-- INSERT INTO ar_device VALUES(2, '000000', 'AR眼镜-002', 'DEV002', 'HoloLens 2', '0', 'AR智能眼镜设备', '0', 103, 1, sysdate(), null, null);
-- 示例区域数据
-- INSERT INTO ar_region VALUES(1, '000000', '主控室', 'REGION001', null, '0', '电厂主控室区域', '0', 103, 1, sysdate(), null, null);
-- INSERT INTO ar_region VALUES(2, '000000', '设备间', 'REGION002', null, '0', '设备机房区域', '0', 103, 1, sysdate(), null, null);
-- 示例点位数据
-- INSERT INTO ar_point VALUES(1, '000000', 1, '主控台', 'POINT001', null, '主控台点位', '0', 103, 1, sysdate(), null, null);
-- INSERT INTO ar_point VALUES(2, '000000', 1, '监控屏', 'POINT002', null, '监控屏点位', '0', 103, 1, sysdate(), null, null);
-- 示例任务模板数据
-- INSERT INTO ar_task VALUES(1, '000000', '日常巡检任务', 'TASK001', 1, 'daily', '0', '每日例行巡检任务', '0', 103, 1, sysdate(), null, null);
-- INSERT INTO ar_task VALUES(2, '000000', '设备检修任务', 'TASK002', 2, 'maintenance', '0', '设备定期检修任务', '0', 103, 1, sysdate(), null, null);
SET FOREIGN_KEY_CHECKS = 1;
-- ----------------------------
-- 说明
-- ----------------------------
-- 1. 本SQL文件适用于 MySQL 8.0+ 版本
-- 2. 所有表均支持多租户功能,通过 tenant_id 字段隔离
-- 3. 所有表均支持逻辑删除,通过 del_flag 字段标识
-- 4. 主键采用雪花ID算法,由应用程序生成,不使用数据库自增
-- 5. 所有表包含标准审计字段: create_dept, create_by, create_time, update_by, update_time
-- 6. ar_step 表支持树形结构,通过 parent_id 和 ancestors 字段实现
-- 7. JSON字段使用 text 类型存储,由 MyBatis-Plus 的 JacksonTypeHandler 处理
-- 8. 索引已根据业务查询场景优化配置
-- 9. 字符集使用 utf8mb4,支持emoji等特殊字符
-- 10. 排序规则使用 utf8mb4_unicode_ci,确保多语言支持
-- ----------------------------
-- 导入说明
-- ----------------------------
-- 执行方式1: 命令行导入
-- mysql -u root -p -D ry-vue < /path/to/ar-inspection-tables.sql
--
-- 执行方式2: MySQL客户端执行
-- mysql> use ry-vue;
-- mysql> source /path/to/ar-inspection-tables.sql;
--
-- 执行方式3: Navicat等GUI工具
-- 打开SQL文件后点击"运行"按钮执行
--
-- 注意事项:
-- - 请确保已创建数据库 ry-vue (或根据实际情况修改数据库名)
-- - 建议先在测试环境验证后再在生产环境执行
-- - 执行前请备份现有数据库
-- - 示例数据默认已注释,如需使用请取消注释
-- ----------------------------
-- 表关系说明
-- ----------------------------
-- ar_device (设备)
-- └── ar_execution (执行记录) - 通过 device_id 关联
--
-- ar_region (区域)
-- ├── ar_point (点位) - 通过 region_id 关联
-- ├── ar_task (任务模板) - 通过 region_id 关联
-- └── ar_execution (执行记录) - 通过 region_id 关联
--
-- ar_point (点位)
-- └── ar_step (巡检步骤) - 通过 point_id 关联
--
-- ar_task (任务模板)
-- ├── ar_step (巡检步骤) - 通过 task_id 关联
-- └── ar_execution (执行记录) - 通过 task_id 关联
--
-- ar_step (巡检步骤)
-- ├── ar_step (父子关系) - 通过 parent_id 关联(树形结构)
-- └── ar_step_record (步骤记录) - 通过 step_id 关联
--
-- ar_execution (执行记录)
-- └── ar_step_record (步骤记录) - 通过 execution_id 关联
--
-- ar_step_record (步骤记录)
-- └── ar_step_media (步骤媒体) - 通过 step_record_id 关联
-- 创建完成!