Compare commits

...

217 Commits
test ... main

Author SHA1 Message Date
gyb 618012c9d4 feat:增加标注分组 2026-03-16 19:28:18 +08:00
gyb 608f9e1ffd feat:增加标注分组 2026-03-16 18:33:21 +08:00
gyb b4a7b290b7 Merge remote-tracking branch 'origin/main' 2026-03-16 11:48:08 +08:00
gyb ad4b0f46d3 feat:增加批量移动接口,优化查询接口逻辑 2026-03-16 11:47:46 +08:00
孙小云 b2f2601f37 添加状态修改接口 2026-03-14 11:39:23 +08:00
孙小云 097682d1b9 xx 2026-03-14 11:03:16 +08:00
孙小云 7e8b721a6e 修改接口 2026-03-14 10:40:17 +08:00
孙小云 fbc1d4fd22 修改状态枚举 2026-03-14 10:17:20 +08:00
孙小云 789a11bf1d 添加API 2026-03-14 09:25:23 +08:00
孙小云 a6f422f4a9 添加字段 2026-03-13 16:17:11 +08:00
孙小云 b15f6954da API 2026-03-13 15:53:53 +08:00
孙小云 e97ab46ec6 添加DTO“ 2026-03-13 15:14:45 +08:00
孙小云 eac9e611c2 xx 2026-03-13 15:01:50 +08:00
gyb 869e33441e feat:增加通过航线id查询航线详情的接口 2026-03-13 14:52:02 +08:00
孙小云 6eeb40c110 修改接口定义 2026-03-13 13:59:42 +08:00
gyb f0ce72b44c Merge remote-tracking branch 'origin/main' 2026-03-13 13:46:17 +08:00
gyb e8ce003cf0 feat:增加通过航线id查询航线详情的接口 2026-03-13 13:45:11 +08:00
孙小云 a8ff1cbd7c xx 2026-03-13 11:53:03 +08:00
孙小云 6318ffae92 修改接口 2026-03-13 11:48:01 +08:00
孙小云 67c071c997 修改对象 2026-03-13 11:44:07 +08:00
孙小云 aae9c3537e 添加参数 2026-03-13 10:52:19 +08:00
孙小云 1290d6a524 修改参数 2026-03-13 10:27:24 +08:00
孙小云 48f53231c9 修改定义 2026-03-13 09:37:16 +08:00
孙小云 72b111b13f 修改接口 2026-03-13 09:15:15 +08:00
孙小云 3c47e50574 Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-03-12 18:45:55 +08:00
孙小云 216b4e5bb3 修改枚举 2026-03-12 18:45:51 +08:00
gyb fbc35af80c feat:提交标注代码 2026-03-12 15:08:56 +08:00
gyb 77202deb58 Merge remote-tracking branch 'origin/main' 2026-03-12 14:17:33 +08:00
gyb b3fd1019f9 feat:提交标注代码 2026-03-12 14:17:08 +08:00
孙小云 174bedac08 修改枚举的处理 2026-03-12 13:10:45 +08:00
孙小云 c256461c59 Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-03-12 09:10:57 +08:00
孙小云 d153ed9780 添加无人机SN号 snNumber 2026-03-12 09:10:14 +08:00
gyb f21a0b32c2 feat:航线编辑接口新的航点动作保存 2026-03-11 13:22:09 +08:00
孙小云 ce96ca3988 添加日志 2026-03-10 16:18:56 +08:00
孙小云 4df11ebcc8 添加自动化配置 2026-03-10 11:34:22 +08:00
孙小云 5a57743e05 添加参数 2026-03-09 17:20:21 +08:00
孙小云 1f0f8841ce 添加接口 2026-03-09 16:35:55 +08:00
孙小云 3f08e8b117 xx 2026-03-09 14:50:03 +08:00
孙小云 e75e390d09 修改接口中的字段定义 2026-03-09 09:58:16 +08:00
孙小云 6cd6cb7167 修改字段类型 2026-03-09 09:50:56 +08:00
孙小云 e6dc3a2936 添加航线路径 2026-03-07 10:37:38 +08:00
孙小云 3e1e4d5e95 添加controller层接口 2026-03-06 14:33:35 +08:00
孙小云 da059bf1ad task 2026-03-06 09:39:43 +08:00
孙小云 3a9b077abe Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-03-06 09:19:47 +08:00
孙小云 af9aa18353 xx 2026-03-06 09:19:43 +08:00
Your Name e72415754a 修改代码 2026-03-05 09:17:46 +08:00
孙小云 3c550c72ea 修改配置 2026-03-04 17:44:42 +08:00
孙小云 db3bc20f7d xx 2026-03-04 17:41:44 +08:00
孙小云 33b880934e Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-03-04 17:25:42 +08:00
孙小云 abd64ed135 添加参数 2026-03-04 17:25:37 +08:00
gyb 93129f4956 feat:增加无人机类型负载属性 2026-03-04 16:41:03 +08:00
gyb a61ea65bff Merge remote-tracking branch 'origin/main' 2026-03-04 16:40:43 +08:00
孙小云 628efcb0b4 修改控制参数 2026-03-04 15:09:12 +08:00
gyb 9905d91ea2 feat:增加无人机类型负载属性 2026-03-04 13:49:11 +08:00
孙小云 1bd4e7e902 Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-03-03 10:00:20 +08:00
孙小云 3953241f97 添加飞控指令 2026-03-03 09:57:15 +08:00
gyb 246693e0ea feat:增加航线属性字段 2026-03-02 15:02:51 +08:00
gyb 8d6424f800 feat:增加空域限时规则 2026-03-02 10:05:48 +08:00
gyb 0a9d5c6df4 Merge remote-tracking branch 'origin/main' 2026-03-02 09:54:01 +08:00
gyb 2b1ac349e0 feat:增加航线限高 2026-03-02 09:53:48 +08:00
孙小云 e20c2f389e 飞控指令 2026-02-28 14:57:24 +08:00
孙小云 81c3ff6bb2 Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-02-28 14:52:08 +08:00
孙小云 7e837dff3f 添加控制指令API 2026-02-28 14:52:00 +08:00
gyb efe420b5a9 feat:调试接口 2026-02-28 13:23:45 +08:00
gyb 249dc49dc5 Merge remote-tracking branch 'origin/main' 2026-02-27 14:44:04 +08:00
gyb 841a51e0ee feat:航线增加无人机类型字段 2026-02-27 14:43:55 +08:00
孙小云 02a018df38 xx 2026-02-27 14:13:54 +08:00
孙小云 0d460b178a 添加接口参数 2026-02-27 14:12:33 +08:00
gyb 19be5b4bf0 feat:无人机类型增加类型唯一值 2026-02-27 09:45:12 +08:00
gyb 8ae8df19b1 feat:无人机类型增加类型唯一值 2026-02-26 17:43:09 +08:00
gyb e72d969862 feat:调整查询航线分组查询接口 2026-02-26 17:33:13 +08:00
gyb 13e98cbcdb feat:调整查询sql 2026-02-26 16:00:41 +08:00
gyb c19a1ad028 feat:增加字段 2026-02-26 15:27:31 +08:00
gyb fe975a6681 feat:增加空域相关功能接口 2026-02-25 15:16:28 +08:00
孙小云 a40346be1f xx 2026-02-24 11:08:08 +08:00
孙小云 d1536c00bd xx 2026-02-24 10:34:22 +08:00
孙小云 2c9ccd6ea4 添加接口定义 2026-02-24 10:28:58 +08:00
孙小云 38c1643bb0 添加默认航线文件 2026-02-24 10:15:52 +08:00
孙小云 222f89a6c1 添加开关仓 2026-02-11 14:51:02 +08:00
孙小云 3d5f3622f7 添加开光仓 2026-02-11 14:50:22 +08:00
孙小云 deab72f3cd Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-02-11 14:49:18 +08:00
孙小云 b81c02d204 添加开关仓指令 2026-02-11 14:49:13 +08:00
gyb 16d52f13af Merge remote-tracking branch 'origin/main' 2026-02-11 13:35:24 +08:00
gyb cb3af6ee0d fit:增加航线分类接口计数 2026-02-11 13:35:17 +08:00
孙小云 c025f161b0 xx 2026-02-11 11:30:57 +08:00
孙小云 d6856ec1c2 xx 2026-02-11 09:16:06 +08:00
孙小云 6ae48aee54 xx 2026-02-10 15:30:50 +08:00
gyb a97844dec1 fit:设备分类接口增加,分类分组-新老写法兼容 2026-02-09 13:49:43 +08:00
gyb ed04b6c4c0 fit:设备分类接口增加,分类分组 2026-02-09 13:44:02 +08:00
gyb 1de86e9dc0 bug:航线一期增加航线查询接口 2026-02-09 08:40:59 +08:00
孙小云 4f7f5a2c94 xx 2026-02-07 09:16:02 +08:00
孙小云 417f51294d xx 2026-02-06 16:56:53 +08:00
孙小云 635a3eeb18 xx 2026-02-06 14:40:43 +08:00
孙小云 577cb0314b 修改网关配置 IOT拓恒配置 2026-02-06 10:54:32 +08:00
孙小云 09874183d9 xx 2026-02-05 17:12:45 +08:00
孙小云 6c676dff9c xx 2026-02-05 17:12:16 +08:00
孙小云 8553e4604b xx 2026-02-05 17:11:50 +08:00
孙小云 92605877f4 xx 2026-02-05 17:11:25 +08:00
孙小云 2efd0a6465 xx 2026-02-05 17:10:06 +08:00
孙小云 54fded65d0 xx 2026-02-05 16:57:46 +08:00
孙小云 d305ee72b8 xx 2026-02-05 16:51:35 +08:00
孙小云 fc5d587b96 xx 2026-02-05 14:46:28 +08:00
孙小云 38946a67cd 优化脚本 2026-02-05 14:01:41 +08:00
孙小云 39ea52589f 修改thingsboard 2026-02-05 13:43:55 +08:00
孙小云 57edab1cb2 xx 2026-02-04 18:25:21 +08:00
孙小云 b0d956084c xx 2026-02-04 17:47:10 +08:00
孙小云 21b8bc2560 xx 2026-02-04 17:43:23 +08:00
孙小云 d19de44668 xx 2026-02-04 17:41:39 +08:00
孙小云 ddd85bc176 xx 2026-02-04 17:25:39 +08:00
孙小云 1dcedf3150 xx 2026-02-04 16:41:19 +08:00
孙小云 32eec0a709 nacos 2026-02-04 16:40:06 +08:00
孙小云 36d0423908 xx 2026-02-04 16:38:47 +08:00
孙小云 de86e0f0b7 xx 2026-02-04 13:10:58 +08:00
gyb e58d28dba1 fit:调整类名及类路径 2026-02-04 11:17:21 +08:00
孙小云 345d50b52f 添加接口和枚举 2026-02-04 09:54:31 +08:00
孙小云 e09fb1ef6f xx 2026-02-03 16:40:22 +08:00
孙小云 907d638a8c xx 2026-02-03 16:37:49 +08:00
孙小云 6c175a7008 xx 2026-02-03 16:34:41 +08:00
孙小云 7b90688164 添加容器日志 2026-02-03 16:18:08 +08:00
孙小云 80304fcfa8 xx 2026-02-03 15:59:09 +08:00
孙小云 59678411af xx 2026-02-03 15:57:41 +08:00
孙小云 da82d8ee17 修改脚本 2026-02-03 15:53:59 +08:00
孙小云 0db6eecceb xx 2026-02-03 09:45:44 +08:00
孙小云 1ef708f925 修改配置 2026-02-02 15:02:46 +08:00
孙小云 11c4fecae2 xx 2026-02-02 14:39:29 +08:00
孙小云 488dfc0454 xx 2026-02-02 14:18:32 +08:00
孙小云 9758e1800b xx 2026-02-02 13:54:36 +08:00
孙小云 1711dd8941 修改网关配置nacos初始化脚本 2026-02-02 13:39:35 +08:00
孙小云 f8a9b2a827 修改网关配置 2026-02-02 13:25:33 +08:00
孙小云 6ff528d9b0 xx 2026-01-31 15:53:44 +08:00
孙小云 1146dd6773 添加脚本 2026-01-31 15:45:34 +08:00
孙小云 231e23329f 添加ruoyi-hxf 2026-01-31 15:40:05 +08:00
孙小云 e305dc513b 添加ruoyi-hxf 2026-01-31 15:33:41 +08:00
孙小云 5732553789 xx 2026-01-31 15:26:37 +08:00
孙小云 8a24fe8ec2 xx 2026-01-31 15:10:06 +08:00
孙小云 05c670a173 添加python构建的支持 2026-01-31 14:55:24 +08:00
孙小云 22b37720ca xx: 2026-01-31 14:33:09 +08:00
孙小云 86fbb0fb80 xx 2026-01-31 13:41:14 +08:00
孙小云 40d757fe59 同步到master 2026-01-31 13:37:51 +08:00
孙小云 a03e2b9f0f xx 2026-01-31 13:37:05 +08:00
孙小云 c174366b75 xx 2026-01-31 13:35:50 +08:00
孙小云 151163031a xx 2026-01-31 13:34:40 +08:00
孙小云 31142dff80 修改枚举 2026-01-31 13:28:30 +08:00
孙小云 3a63460966 feat: 将 hyf-backend 添加为 Git 子模块
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 11:18:28 +08:00
孙小云 9c0cfbc185 xx 2026-01-30 17:55:11 +08:00
孙小云 da9b5b4378 修改SQL 2026-01-30 17:49:06 +08:00
孙小云 58c17c391a 添加websocket 2026-01-30 17:45:45 +08:00
gyb 4af39290ef fit:修复bug 2026-01-30 11:48:50 +08:00
高大 df640c3582 fit:增加无人机厂商分组接口 2026-01-29 17:00:25 +08:00
高大 d6cd0f003b Merge remote-tracking branch 'origin/main' 2026-01-29 16:45:39 +08:00
高大 fa3a76f6e8 fit:增加数据字典对外接口 2026-01-29 16:45:01 +08:00
孙小云 be476f39e7 xx 2026-01-29 11:55:07 +08:00
孙小云 24cf92c0b8 remove: 移除 hyf-backend 子模块
- 从项目中移除 hyf-backend 子模块
- 更新 .gitmodules 配置
- 删除相关 docker 配置文件

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 11:49:30 +08:00
孙小云 a207ceaa06 feat: 添加 hyf-backend 子模块
添加 hyf-backend 作为 Git 子模块,用于后端服务扩展。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 11:17:52 +08:00
孙小云 c9148fad9f 添加充放电状态 2026-01-29 10:18:41 +08:00
孙小云 be438fe3dc 添加充放电状态 2026-01-29 10:14:43 +08:00
高大 c9f43207a1 fix:优化convert 2026-01-28 20:16:00 +08:00
高大 82a754b403 Merge remote-tracking branch 'origin/main' 2026-01-28 16:36:13 +08:00
高大 437c5e4f17 fix:增加无人机/遥控器/机场 类型枚举表 2026-01-28 16:35:44 +08:00
孙小云 d34c9960df 添加充放电状态 2026-01-28 16:21:09 +08:00
孙小云 87bf0d1719 xx 2026-01-28 16:00:22 +08:00
孙小云 1485c874c3 添加充放电状态 2026-01-28 15:57:01 +08:00
孙小云 bedad1228b 添加充放电状态 2026-01-28 15:52:20 +08:00
孙小云 f67d2cfaf3 修改剩余里程的获取方式 2026-01-28 13:05:59 +08:00
孙小云 4c31441973 修改剩余里程的获取方式 2026-01-28 09:59:37 +08:00
孙小云 ec2dbd8cf7 大疆配置 2026-01-28 09:40:31 +08:00
高大 616838a9a8 Merge remote-tracking branch 'origin/main' 2026-01-28 09:22:05 +08:00
高大 e15423b9f7 fix:增加自动航线名称的逻辑 2026-01-28 09:19:56 +08:00
孙小云 f52c64f3a6 修改rtk的获取 2026-01-28 09:07:50 +08:00
孙小云 1211cfcdbd 添加失败通知 2026-01-27 18:14:28 +08:00
孙小云 2560945192 添加失败通知 2026-01-27 17:49:23 +08:00
孙小云 65ee1cf1ba 添加失败通知 2026-01-27 17:42:27 +08:00
高大 f553b46b7b fix:修复编译报错 2026-01-27 17:31:04 +08:00
孙小云 838a198804 添加webhook 2026-01-27 14:39:51 +08:00
孙小云 1ab8622ca6 Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-01-27 14:17:58 +08:00
孙小云 6af0532185 添加webhook 2026-01-27 14:17:44 +08:00
高大 f99c25cede fix:修复编译报错 2026-01-27 13:26:33 +08:00
高大 027ec15c4b Merge remote-tracking branch 'origin/main' 2026-01-27 11:34:30 +08:00
高大 0e8eb02885 fix:修复增加控制层BaseException处理 2026-01-27 11:33:19 +08:00
孙小云 e037e0e11c 添加minio的启动 2026-01-26 16:51:39 +08:00
孙小云 d4c00e552b 添加minio的启动 2026-01-26 16:50:50 +08:00
孙小云 8d9c9ab8f8 添加minio的启动 2026-01-26 16:40:41 +08:00
孙小云 4bd07e816b 添加minio的启动 2026-01-26 16:36:28 +08:00
孙小云 dc662276c2 添加minio的启动 2026-01-26 16:19:12 +08:00
高大 e295ad945b Merge remote-tracking branch 'origin/main' 2026-01-26 15:50:16 +08:00
高大 0eb7cce258 fix:修复接口500的问题,增加BaseController getDataTable 的空表处理 2026-01-26 15:47:48 +08:00
孙小云 4d78110ee4 添加minio的启动 2026-01-26 15:47:40 +08:00
孙小云 31b0e4e158 xx 2026-01-26 15:24:50 +08:00
孙小云 381dcefe8a xx 2026-01-26 15:20:40 +08:00
孙小云 85c1f6f3c0 xx 2026-01-26 15:16:22 +08:00
孙小云 b4f2b37c9f 修改 thingsboard 部署 2026-01-26 14:34:32 +08:00
孙小云 74a3a8ad6d x 2026-01-26 14:22:37 +08:00
孙小云 6a19842ad3 添加 wvp-web html 目录占位文件
确保 docker/wvp/web/html 目录被 Git 跟踪,用于部署时复制前端构建产物

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-26 14:21:21 +08:00
孙小云 ef5032d2b8 修改 thingsboard 部署 2026-01-26 14:12:59 +08:00
孙小云 1f5ba130fb 修改 thingsboard 部署 2026-01-26 14:04:19 +08:00
孙小云 a0fe9edf59 修改 thingsboard 部署 2026-01-26 14:01:22 +08:00
孙小云 c5c65c2a46 修改 thingsboard 部署 2026-01-26 13:49:12 +08:00
孙小云 4c97b8655b 修改 thingsboard 部署 2026-01-26 13:39:12 +08:00
孙小云 e7197e3d19 修改WVP 前端部署流程 2026-01-26 13:34:58 +08:00
孙小云 c1ba024d8d 修改WVP 前端部署流程 2026-01-26 13:27:53 +08:00
孙小云 11ad503687 修改WVP 前端部署流程 2026-01-26 13:25:39 +08:00
孙小云 fe7c7e0146 修改WVP 前端打包 2026-01-26 13:17:49 +08:00
孙小云 d00aec5944 修改WVP 前端打包 2026-01-26 13:15:17 +08:00
孙小云 6cd3e13c39 修改WVP打包 2026-01-26 12:24:50 +08:00
孙小云 bf15fb25ad 修改WVP打包 2026-01-26 12:15:55 +08:00
孙小云 c851677c61 修改WVP打包 2026-01-26 11:45:36 +08:00
孙小云 b7ab7d8330 修改WVP打包 2026-01-26 11:14:23 +08:00
孙小云 f0adb30707 修改WVP打包 2026-01-26 11:03:53 +08:00
孙小云 4ea396320b 修改WVP打包 2026-01-26 10:32:35 +08:00
孙小云 06935d4d0d 修改WVP打包 2026-01-26 10:32:31 +08:00
孙小云 301c273a6f 修改WVP打包 2026-01-26 10:08:38 +08:00
孙小云 88ee853d5e delete id 2026-01-26 09:01:49 +08:00
孙小云 4cc69b2376 Merge branch 'main' of http://th.local.t-aaron.com:13000/THENG/a-cloud-all 2026-01-24 17:24:01 +08:00
孙小云 87a68fd630 group/switch 参数添加支持列表 2026-01-24 17:23:45 +08:00
高大 b2a7748cc3 fix:优化航线管理航线上传逻辑 2026-01-24 17:18:11 +08:00
高大 9069bd5fa2 feat:文件服务补充feign接口-增加流式调用 2026-01-24 16:13:03 +08:00
孙小云 c2a9a16a3a 修改代码ruoyi-file-prod.yml 配置 2026-01-24 15:18:25 +08:00
129 changed files with 7693 additions and 243 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,153 @@
#!/bin/bash
# ============================================================
# 清理没有关联机场的大疆无人机
# ============================================================
set -e
echo "=========================================="
echo "清理没有关联机场的大疆无人机"
echo "=========================================="
# 数据库配置
DB_CONTAINER="ruoyi-mysql"
DB_USER="root"
DB_PASSWORD="password"
DB_NAME="ry-cloud"
echo ""
echo "第一步:查询所有大疆无人机(用于确认)"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn
FROM device_aircraft a
LEFT JOIN device_device d ON a.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang'
ORDER BY a.aircraft_id;
"
echo ""
echo "第二步:查询没有关联机场的大疆无人机"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn,
d.iot_device_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
ORDER BY a.aircraft_id;
"
echo ""
echo "=========================================="
echo "即将删除以上无人机记录"
echo "请确认是否继续?(y/n)"
read -r confirm
if [[ ! $confirm =~ ^[Yy]$ ]]; then
echo "操作已取消"
exit 0
fi
echo ""
echo "第三步:删除无人机挂载关联表中的记录"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
DELETE FROM device_aircraft_payload
WHERE aircraft_id IN (
SELECT a.aircraft_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
);
"
echo "无人机挂载关联记录删除完成"
echo ""
echo "第四步:删除机场无人机关联表中的记录"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
DELETE FROM device_dock_aircraft
WHERE aircraft_id IN (
SELECT a.aircraft_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
);
"
echo "机场无人机关联记录删除完成"
echo ""
echo "第五步:删除无人机表中的记录"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
DELETE FROM device_aircraft
WHERE aircraft_id IN (
SELECT temp.aircraft_id
FROM (
SELECT a.aircraft_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
) AS temp
);
"
echo "无人机记录删除完成"
echo ""
echo "第六步:验证删除结果"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang'
ORDER BY a.aircraft_id;
"
echo ""
echo "统计验证:"
echo "=========================================="
docker exec ${DB_CONTAINER} mysql -u${DB_USER} -p${DB_PASSWORD} ${DB_NAME} -e "
SELECT
'大疆无人机总数' AS statistic_name,
COUNT(*) AS count
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang'
UNION ALL
SELECT
'大疆机场总数' AS statistic_name,
COUNT(*) AS count
FROM device_dock dock
INNER JOIN device_device d ON dock.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang';
"
echo ""
echo "=========================================="
echo "清理完成"
echo "=========================================="

View File

@ -0,0 +1,114 @@
-- ============================================================
-- 清理没有关联机场的大疆无人机
-- ============================================================
-- 问题大疆的机场是7个无人机是10个有3个无人机没有关联机场
-- 解决方案:删除没有关联机场的大疆无人机记录
-- ============================================================
-- 第一步:查询所有大疆厂商的无人机(用于确认)
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn
FROM device_aircraft a
LEFT JOIN device_device d ON a.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang'
ORDER BY a.aircraft_id;
-- 第二步:查询没有关联机场的大疆无人机
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn,
d.iot_device_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
ORDER BY a.aircraft_id;
-- 第三步:确认删除前,再次检查(谨慎操作)
-- 执行此查询后,请确认这些无人机确实需要删除
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn,
d.iot_device_id,
'将被删除' AS action
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL;
-- 第四步:删除没有关联机场的大疆无人机(谨慎操作!)
-- 删除顺序:
-- 1. 先删除无人机挂载关联表中的记录
-- 2. 再删除机场无人机关联表中的记录
-- 3. 最后删除无人机表中的记录
-- 4.1 删除无人机挂载关联表中的记录
DELETE FROM device_aircraft_payload
WHERE aircraft_id IN (
SELECT a.aircraft_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
);
-- 4.2 删除机场无人机关联表中的记录(如果有)
DELETE FROM device_dock_aircraft
WHERE aircraft_id IN (
SELECT a.aircraft_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
);
-- 4.3 删除无人机表中的记录
DELETE FROM device_aircraft
WHERE aircraft_id IN (
SELECT temp.aircraft_id
FROM (
SELECT a.aircraft_id
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
LEFT JOIN device_dock_aircraft da ON a.aircraft_id = da.aircraft_id
WHERE d.device_manufacturer = 'dajiang'
AND da.id IS NULL
) AS temp
);
-- 第五步:验证删除结果
SELECT
a.aircraft_id,
a.aircraft_name,
d.device_manufacturer,
d.device_sn
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang'
ORDER BY a.aircraft_id;
-- 统计验证
SELECT
'大疆无人机总数' AS statistic_name,
COUNT(*) AS count
FROM device_aircraft a
INNER JOIN device_device d ON a.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang'
UNION ALL
SELECT
'大疆机场总数' AS statistic_name,
COUNT(*) AS count
FROM device_dock dock
INNER JOIN device_device d ON dock.device_id = d.device_id
WHERE d.device_manufacturer = 'dajiang';

View File

@ -12,6 +12,8 @@ main_repository:
monitor:
poll_interval: 10 # 轮询间隔(秒)
enabled_repos: [] # 空数组表示监听所有仓库,或指定具体仓库名称列表
watch_tags: false # 是否监听 tag 变化(打 tag 时触发部署)
tag_pattern: "v*" # 监听的 tag 模式,支持通配符(如 v*、release-*
# 部署配置
deploy:
@ -22,6 +24,21 @@ logging:
file: .devops/logs/devops.log
max_size: 10485760 # 10MB
# 钉钉通知配置
dingtalk:
enabled: true # 是否启用钉钉通知
access_token: fccb17a6c74ca862d7994630562d9d3d5be7b60d012d53dce32c64c566b9a3de
secret: SEC54a73636473f324b8f77741ee41ee278d22eba4af898bba739c11183d851c8b0
# 数据库配置(用于数据库管理页面)
database:
container_name: ruoyi-mysql # MySQL 容器名称
host: localhost
port: 3306
user: root
password: password
charset: utf8mb4
# 基础设施服务配置(只部署一次)
infrastructure:
- name: ruoyi-mysql
@ -30,17 +47,38 @@ infrastructure:
- cp sql/ry_20250523.sql docker/mysql/db/
- cp sql/ry_config_20250902.sql docker/mysql/db/
- cp sql/wvp.sql docker/mysql/db/
- cp sql/privileges.sql docker/mysql/db/
wait_time: 30 # MySQL 需要更长时间初始化
- name: ruoyi-redis
docker_service: ruoyi-redis
wait_time: 10 # Redis 启动较快
- name: ruoyi-minio
docker_service: ruoyi-minio
wait_time: 15 # MinIO 对象存储服务
- name: ruoyi-nacos
docker_service: ruoyi-nacos
wait_time: 20 # Nacos 需要等待 MySQL 就绪
- name: zlmediakit
docker_service: zlmediakit
wait_time: 10 # 流媒体服务器启动较快
- name: wvp-pro
docker_service: wvp-pro
pre_deploy_commands:
- cd wvpjar && mvn clean package -DskipTests
- cp wvpjar/target/wvp-pro-*.jar docker/wvp/wvpjar/jar/
wait_time: 30 # WVP 后端需要等待 MySQL、Redis 和 zlmediakit
- name: wvp-web
docker_service: wvp-web
pre_deploy_commands:
- cd wvpweb && npm install && npm run build:prod
- cp -r wvpweb/dist docker/wvp/web/html/dist
wait_time: 10 # Nginx 启动较快
# Git 仓库配置
repositories:
# 认证服务
@ -49,7 +87,7 @@ repositories:
path: ruoyi-auth
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/auth/jar
docker_service: ruoyi-auth
@ -60,7 +98,7 @@ repositories:
path: ruoyi-gateway
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/gateway/jar
docker_service: ruoyi-gateway
@ -88,13 +126,24 @@ repositories:
docker_path: docker/a_th_web/html/dist
docker_service: ruoyi-hyf
- name: ruoyi-hxf
url: http://th.local.t-aaron.com:13000/THENG/a_th_web.git
path: a_th_web
type: nodejs
build_commands:
- pnpm install
- pnpm run build-prod:hxf
artifact_path: apps/tuoheng_hxf_web/dist
docker_path: docker/b_th_web/html/dist
docker_service: ruoyi-hxf
# 系统服务
- name: ruoyi-system
url: http://th.local.t-aaron.com:13000/THENG/a-ruoyi-system.git
path: ruoyi-modules/ruoyi-system
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/system/jar
docker_service: ruoyi-modules-system
@ -105,7 +154,7 @@ repositories:
path: ruoyi-modules/ruoyi-file
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/file/jar
docker_service: ruoyi-modules-file
@ -116,7 +165,7 @@ repositories:
path: ruoyi-modules/ruoyi-gen
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/gen/jar
docker_service: ruoyi-modules-gen
@ -127,7 +176,7 @@ repositories:
path: ruoyi-modules/ruoyi-job
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/job/jar
docker_service: ruoyi-modules-job
@ -138,7 +187,7 @@ repositories:
path: ruoyi-visual/ruoyi-monitor
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/visual/monitor/jar
docker_service: ruoyi-visual-monitor
@ -149,7 +198,7 @@ repositories:
path: ruoyi-modules/tuoheng-device
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/device/jar
docker_service: tuoheng-modules-device
@ -160,7 +209,7 @@ repositories:
path: ruoyi-modules/tuoheng-approval
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/approval/jar
docker_service: tuoheng-modules-approval
@ -171,7 +220,7 @@ repositories:
path: ruoyi-modules/tuoheng-airline
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/airline/jar
docker_service: tuoheng-modules-airline
@ -182,7 +231,7 @@ repositories:
path: ruoyi-modules/tuoheng-task
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/task/jar
docker_service: tuoheng-modules-task
@ -193,7 +242,7 @@ repositories:
path: ruoyi-modules/tuoheng-fms
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/fms/jar
docker_service: tuoheng-modules-fms
@ -204,7 +253,41 @@ repositories:
path: ruoyi-modules/tuoheng-media
type: java
build_commands:
- mvn clean package -DskipTests
- export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/ruoyi/modules/media/jar
docker_service: tuoheng-modules-media
# WVP 后端服务
- name: wvp-pro
url: http://th.local.t-aaron.com:13000/THENG/a-wvp-java.git
path: wvpjar
type: java
build_commands:
- cd wvpjar && export JAVA_HOME=/data/jdk/jdk17 && export PATH=$JAVA_HOME/bin:$PATH && mvn clean package -DskipTests -Dmaven.compiler.source=17 -Dmaven.compiler.target=17
artifact_path: target/*.jar
docker_path: docker/wvp/wvpjar/jar
docker_service: wvp-pro
# WVP 前端服务
- name: wvp-web
url: http://th.local.t-aaron.com:13000/THENG/a-wvp-web.git
path: wvpweb
type: nodejs
build_commands:
- npm install
- npm run build:prod
artifact_path: dist
docker_path: docker/wvp/web/html/dist
docker_service: wvp-web
# HYF 后端服务
- name: hyf-backend
url: http://th.local.t-aaron.com:13000/THENG/hyf-backend.git
path: hyf-backend
type: python
build_commands: [] # Python 项目不需要构建,直接复制源码
artifact_path: . # 复制整个项目目录
docker_path: docker/hyf_backend/src
docker_service: hyf-backend
docker_build: true # 需要重新构建镜像

File diff suppressed because it is too large Load Diff

View File

@ -38,9 +38,30 @@ echo "✓ Python3 已安装: $(python3 --version)"
# 4. 检查 Python 依赖
echo ""
echo "[步骤 4/7] 检查 Python 依赖..."
# 检查并安装 pip
if ! python3 -m pip --version &> /dev/null; then
echo "pip 未安装,正在安装 pip..."
curl -sS https://bootstrap.pypa.io/get-pip.py | python3 - --user --break-system-packages 2>/dev/null || \
curl -sS https://bootstrap.pypa.io/get-pip.py | python3 - --user
echo "✓ pip 安装完成"
fi
# 使用 python3 -m pip 代替 pip3添加 --break-system-packages 标志
if ! python3 -c "import yaml" 2>/dev/null; then
echo "安装 PyYAML..."
pip3 install --user PyYAML
python3 -m pip install --user --break-system-packages PyYAML 2>/dev/null || \
python3 -m pip install --user PyYAML
fi
if ! python3 -c "import flask" 2>/dev/null; then
echo "安装 Flask..."
python3 -m pip install --user --break-system-packages flask 2>/dev/null || \
python3 -m pip install --user flask
fi
if ! python3 -c "import pymysql" 2>/dev/null; then
echo "安装 PyMySQL用于数据库管理功能..."
python3 -m pip install --user --break-system-packages pymysql 2>/dev/null || \
python3 -m pip install --user pymysql
fi
echo "✓ Python 依赖检查完成"

255
.devops/scripts/dingtalk.py Normal file
View File

@ -0,0 +1,255 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
钉钉 Webhook 通知模块
用于发送构建和部署通知到钉钉群
"""
import time
import hmac
import hashlib
import base64
import urllib.parse
import urllib.request
import json
from datetime import datetime
class DingTalkNotifier:
"""钉钉通知器"""
def __init__(self, access_token, secret=None):
"""
初始化钉钉通知器
Args:
access_token: 钉钉机器人的 access_token
secret: 钉钉机器人的加签密钥可选
"""
self.access_token = access_token
self.secret = secret
self.base_url = "https://oapi.dingtalk.com/robot/send"
def _generate_sign(self, timestamp):
"""
生成钉钉加签
Args:
timestamp: 当前时间戳毫秒
Returns:
签名字符串
"""
if not self.secret:
return None
string_to_sign = f'{timestamp}\n{self.secret}'
string_to_sign_enc = string_to_sign.encode('utf-8')
secret_enc = self.secret.encode('utf-8')
hmac_code = hmac.new(
secret_enc,
string_to_sign_enc,
digestmod=hashlib.sha256
).digest()
sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
return sign
def _build_url(self):
"""
构建完整的 webhook URL
Returns:
完整的 URL 字符串
"""
url = f"{self.base_url}?access_token={self.access_token}"
if self.secret:
timestamp = str(round(time.time() * 1000))
sign = self._generate_sign(timestamp)
url += f"&timestamp={timestamp}&sign={sign}"
return url
def send_text(self, content, at_mobiles=None, at_all=False):
"""
发送文本消息
Args:
content: 消息内容
at_mobiles: @的手机号列表
at_all: 是否@所有人
Returns:
发送结果 (True/False)
"""
data = {
"msgtype": "text",
"text": {
"content": content
},
"at": {
"atMobiles": at_mobiles or [],
"isAtAll": at_all
}
}
return self._send_request(data)
def send_build_success(self, repo_name, branch, commit_hash, duration):
"""
发送构建成功通知
Args:
repo_name: 仓库名称
branch: 分支名称
commit_hash: 提交哈希
duration: 构建耗时
Returns:
发送结果 (True/False)
"""
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
title = f"✅ 构建成功 - {repo_name}"
text = f"""### ✅ 构建成功
**仓库**: {repo_name}
**分支**: {branch}
**提交**: {commit_hash[:8]}
**耗时**: {duration:.1f}
**时间**: {now}
---
构建和部署已完成
"""
return self.send_markdown(title, text)
def send_build_failure(self, repo_name, branch, commit_hash, error_msg, at_all=False):
"""
发送构建失败通知
Args:
repo_name: 仓库名称
branch: 分支名称
commit_hash: 提交哈希
error_msg: 错误信息
at_all: 是否@所有人
Returns:
发送结果 (True/False)
"""
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
title = f"❌ 构建失败 - {repo_name}"
# 限制错误信息长度
error_display = error_msg[:500] if len(error_msg) > 500 else error_msg
text = f"""### ❌ 构建失败
**仓库**: {repo_name}
**分支**: {branch}
**提交**: {commit_hash[:8]}
**时间**: {now}
**错误信息**:
```
{error_display}
```
---
请检查日志并修复问题
"""
return self.send_markdown(title, text, at_all=at_all)
def send_build_start(self, repo_name, branch, commit_hash, commit_message=None, server_ip=None):
"""
发送构建开始通知
Args:
repo_name: 仓库名称
branch: 分支名称
commit_hash: 提交哈希
commit_message: 提交消息可选
server_ip: 服务器IP可选
Returns:
发送结果 (True/False)
"""
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
title = f"🚀 构建开始 - {repo_name}"
# 构建消息内容
text = f"""### 🚀 构建开始
**仓库**: {repo_name}
**分支**: {branch}
**提交**: {commit_hash[:8]}"""
if commit_message:
text += f"\n**消息**: {commit_message}"
if server_ip:
text += f"\n**服务器**: {server_ip}"
text += f"""
**时间**: {now}
---
构建任务已启动请稍候...
"""
return self.send_markdown(title, text)
def _send_request(self, data):
"""
发送 HTTP 请求到钉钉 webhook
Args:
data: 请求数据字典
Returns:
发送结果 (True/False)
"""
try:
url = self._build_url()
headers = {'Content-Type': 'application/json'}
json_data = json.dumps(data).encode('utf-8')
req = urllib.request.Request(url, data=json_data, headers=headers)
response = urllib.request.urlopen(req, timeout=10)
result = json.loads(response.read().decode('utf-8'))
if result.get('errcode') == 0:
return True
else:
print(f"钉钉通知发送失败: {result.get('errmsg')}")
return False
except Exception as e:
print(f"钉钉通知发送异常: {e}")
return False
def send_markdown(self, title, text, at_mobiles=None, at_all=False):
"""
发送 Markdown 消息
Args:
title: 消息标题
text: Markdown 格式的消息内容
at_mobiles: @的手机号列表
at_all: 是否@所有人
Returns:
发送结果 (True/False)
"""
data = {
"msgtype": "markdown",
"markdown": {
"title": title,
"text": text
},
"at": {
"atMobiles": at_mobiles or [],
"isAtAll": at_all
}
}
return self._send_request(data)

View File

@ -94,6 +94,11 @@ class Logger:
"""输出警告日志"""
cls._write_log('WARN', message)
@classmethod
def warning(cls, message):
"""输出警告日志warn 的别名)"""
cls._write_log('WARN', message)
@classmethod
def debug(cls, message):
"""输出调试日志"""

View File

@ -22,7 +22,7 @@ def run_maven(work_dir, maven_commands, source_path, target_dir):
target_dir: 复制的目标目录
返回
bool: 成功返回 True失败返回 False
tuple: (success: bool, error_message: str) 成功返回 (True, "")失败返回 (False, "错误信息")
"""
try:
# 转换为绝对路径
@ -36,13 +36,15 @@ def run_maven(work_dir, maven_commands, source_path, target_dir):
# 检查目录是否存在
if not work_dir.exists():
Logger.error(f"目录不存在: {work_dir}")
return False
error_msg = f"目录不存在: {work_dir}"
Logger.error(error_msg)
return False, error_msg
# 执行 Maven 命令
if not Logger.run_command(maven_commands, work_dir):
Logger.error("Maven 打包失败")
return False
error_msg = "Maven 编译失败,请查看日志获取详细错误信息"
Logger.error(error_msg)
return False, error_msg
Logger.info("Maven 打包成功")
@ -61,8 +63,9 @@ def run_maven(work_dir, maven_commands, source_path, target_dir):
# 复制文件
files = glob.glob(str(source_full_path))
if not files:
Logger.error(f"未找到构建产物: {source_full_path}")
return False
error_msg = f"未找到构建产物: {source_full_path}"
Logger.error(error_msg)
return False, error_msg
for file in files:
file_path = Path(file)
@ -74,8 +77,9 @@ def run_maven(work_dir, maven_commands, source_path, target_dir):
Logger.info("构建产物复制成功")
Logger.info("Maven 打包和复制完成")
return True
return True, ""
except Exception as e:
Logger.error(f"Maven 打包异常: {e}")
return False
error_msg = f"Maven 打包异常: {str(e)}"
Logger.error(error_msg)
return False, error_msg

View File

@ -22,7 +22,7 @@ def run_npm(work_dir, npm_commands, source_dir, target_dir):
target_dir: 复制的目标目录
返回
bool: 成功返回 True失败返回 False
tuple: (success: bool, error_message: str) 成功返回 (True, "")失败返回 (False, "错误信息")
"""
try:
# 转换为绝对路径
@ -36,13 +36,15 @@ def run_npm(work_dir, npm_commands, source_dir, target_dir):
# 检查目录是否存在
if not work_dir.exists():
Logger.error(f"目录不存在: {work_dir}")
return False
error_msg = f"目录不存在: {work_dir}"
Logger.error(error_msg)
return False, error_msg
# 执行 NPM 命令
if not Logger.run_command(npm_commands, work_dir):
Logger.error("NPM 打包失败")
return False
error_msg = "NPM/PNPM 编译失败,请查看日志获取详细错误信息"
Logger.error(error_msg)
return False, error_msg
Logger.info("NPM 打包成功")
@ -57,8 +59,9 @@ def run_npm(work_dir, npm_commands, source_dir, target_dir):
# 检查源目录是否存在
if not source_full_path.exists():
Logger.error(f"源目录不存在: {source_full_path}")
return False
error_msg = f"源目录不存在: {source_full_path}"
Logger.error(error_msg)
return False, error_msg
# 清空目标目录(保留 .gitkeep
target_path = Path(target_dir)
@ -84,8 +87,9 @@ def run_npm(work_dir, npm_commands, source_dir, target_dir):
Logger.info("构建产物复制成功")
Logger.info("NPM 打包和复制完成")
return True
return True, ""
except Exception as e:
Logger.error(f"NPM 打包异常: {e}")
return False
error_msg = f"NPM 打包异常: {str(e)}"
Logger.error(error_msg)
return False, error_msg

View File

@ -44,12 +44,20 @@ if [ -z "$VIRTUAL_ENV" ]; then
echo "警告: 未找到 PyYAML尝试安装..."
pip3 install --user PyYAML || echo "请手动安装: pip3 install --user PyYAML"
fi
if ! python3 -c "import flask" 2>/dev/null; then
echo "警告: 未找到 Flask尝试安装..."
pip3 install --user flask || echo "请手动安装: pip3 install --user flask"
fi
else
echo "检测到虚拟环境: $VIRTUAL_ENV"
if ! python3 -c "import yaml" 2>/dev/null; then
echo "安装 PyYAML..."
pip install PyYAML
fi
if ! python3 -c "import flask" 2>/dev/null; then
echo "安装 Flask..."
pip install flask
fi
fi
# 进入项目根目录

14
.gitignore vendored
View File

@ -41,6 +41,7 @@ nbdist/
*.log
*.xml.versionsBackup
*.swp
.DS_Store
!*/build/*.java
!*/build/*.html
@ -56,6 +57,12 @@ docker/minio/data/
docker/minio/config/
docker/nginx/html/dist/*
!docker/nginx/html/dist/.gitkeep
docker/wvp/web/html/dist/*
!docker/wvp/web/html/dist/.gitkeep
docker/a_th_web/html/dist/*
!docker/a_th_web/html/dist/.gitkeep
docker/b_th_web/html/dist/*
!docker/b_th_web/html/dist/.gitkeep
runtime/*
!runtime/.gitkeep
*.jar
@ -63,5 +70,12 @@ runtime/*
!.mvn/wrapper/maven-wrapper.jar
!docker/ruoyi/**/jar/.gitkeep
# hyf_backend source files (except .readme)
docker/hyf_backend/src/*
!docker/hyf_backend/src/.readme
docker/hyf_backend/pgdata/
docker/hyf_backend/data/
docker/hyf_backend/logs/
# K8s generated files
k8s/**/secrets/

3
.gitmodules vendored
View File

@ -56,3 +56,6 @@
[submodule "thingsboard"]
path = thingsboard
url = http://221.226.114.142:13000/THENG/thingsboard.git
[submodule "hyf-backend"]
path = hyf-backend
url = http://th.local.t-aaron.com:13000/THENG/hyf-backend.git

@ -1 +1 @@
Subproject commit b3c50f6011df27cf3993ddf8a56cb1638b6abb04
Subproject commit 0a0b9741cca600b27b6ab15905f02ae3d71e29e2

View File

@ -6,4 +6,3 @@ RTP_TCP_PORT=10000
RTP_UDP_PORT=10000
WEBRTC_UDP_PORT1=8000
WEBRTC_UDP_PORT2=9000
RESTART_POLICY=unless-stopped

View File

@ -14,18 +14,47 @@ http {
listen 80;
server_name localhost;
location /waypoint/ {
root /home/ruoyi/projects/ruoyi-ui;
}
location / {
root /home/ruoyi/projects/ruoyi-ui;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
# WebSocket 配置 (必须放在 /prod-api/ 之前)
location /prod-api/websocket/ {
proxy_pass http://ruoyi-gateway:8080/websocket/;
# WebSocket 必需的配置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 其他代理头
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 超时设置WebSocket 长连接)
proxy_connect_timeout 60s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
}
location /prod-api/{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi-gateway:8080/;
# 支持长时间命令执行
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
location /minio/ {
@ -40,6 +69,14 @@ http {
proxy_pass http://ruoyi-minio:9000/;
}
# WVP-PRO 配置
location /wvp/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi-gateway:8080/wvp/;
}
# 避免actuator暴露
if ($uri ~ "/actuator") {
return 403;

View File

@ -13,3 +13,5 @@ WORKDIR /home/ruoyi/projects/ruoyi-ui
COPY ./conf/nginx.conf /etc/nginx/nginx.conf
# 复制html文件到路径
COPY ./html/dist /home/ruoyi/projects/ruoyi-ui
# 复制waypoint文件到路径
COPY ./waypoint/default.waypoints /home/ruoyi/projects/ruoyi-ui/waypoint/default.waypoints

0
docker/a_th_web/html/dist/.gitkeep vendored Normal file
View File

View File

@ -0,0 +1,3 @@
QGC WPL 110
0 1 0 16 0 0 0 0 31.8293035 118.7635779 100.000000 1
1 0 3 22 0.00000000 0.00000000 0.00000000 0.00000000 31.82938020 118.76653610 100.000000 1

View File

@ -0,0 +1,73 @@
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location / {
root /home/ruoyi/projects/ruoyi-ui;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
# WebSocket 配置 (必须放在 /prod-api/ 之前)
location /prod-api/websocket/ {
proxy_pass http://ruoyi-gateway:8080/websocket/;
# WebSocket 必需的配置
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 其他代理头
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 超时设置WebSocket 长连接)
proxy_connect_timeout 60s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
}
location /prod-api/{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi-gateway:8080/;
}
location /minio/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass http://ruoyi-minio:9000/;
}
# 避免actuator暴露
if ($uri ~ "/actuator") {
return 403;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

View File

@ -0,0 +1,15 @@
# 基础镜像
FROM registry.t-aaron.com/nginx:latest
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi/projects/ruoyi-ui
# 创建目录
RUN mkdir -p /home/ruoyi/projects/ruoyi-ui
# 指定路径
WORKDIR /home/ruoyi/projects/ruoyi-ui
# 复制conf文件到路径
COPY ./conf/nginx.conf /etc/nginx/nginx.conf
# 复制html文件到路径
COPY ./html/dist /home/ruoyi/projects/ruoyi-ui

0
docker/b_th_web/html/dist/.gitkeep vendored Normal file
View File

View File

@ -62,3 +62,9 @@ cp ../ruoyi-modules/tuoheng-fms/target/tuoheng-fms.jar ./ruoyi/modules/fms/jar
echo "begin copy tuoheng-media "
cp ../ruoyi-modules/tuoheng-media/target/tuoheng-media.jar ./ruoyi/modules/media/jar
echo "begin copy wvp-jar"
cp ../wvpjar/target/wvp-pro-2.7.4.jar ./wvp/wvpjar/jar
echo "begin copy wvp-web"
cp -r ../wvpweb/dist/* ./wvp/web/html/dist/

View File

@ -50,6 +50,9 @@ docker-compose build --no-cache tuoheng-modules-airline && docker-compose up -d
docker-compose build --no-cache tuoheng-modules-task && docker-compose up -d tuoheng-modules-task
docker-compose build --no-cache tuoheng-modules-fms && docker-compose up -d tuoheng-modules-fms
docker-compose build --no-cache tuoheng-modules-media && docker-compose up -d tuoheng-modules-media
# cp ../wvpjar/target/wvp-pro-2.7.4.jar ./wvp/wvpjar/jar/
docker-compose build --no-cache wvp-pro && docker-compose up -d wvp-pro
docker-compose build --no-cache wvp-web && docker-compose up -d wvp-web
# 关闭所有环境/模块
stop(){
@ -83,3 +86,14 @@ case "$1" in
usage
;;
esac
# cd /Users/sunpeng/workspace/hyf_project/RuoYi-Cloud/wvpweb
#
# # 1. 安装依赖(如果还没安装)
# npm install
#
# # 2. 生产环境打包
# npm run build:prod
#
# 打包完成后,生成的文件会在 dist 目录中。

View File

@ -103,6 +103,24 @@ services:
- ruoyi-gateway
links:
- ruoyi-gateway
ruoyi-hxf:
container_name: ruoyi-hxf
image: hxf-runtime
build:
context: ./b_th_web
environment:
- TZ=Asia/Shanghai
ports:
- "9898:80"
volumes:
- ./b_th_web/html/dist:/home/ruoyi/projects/ruoyi-ui
- ./b_th_web/conf/nginx.conf:/etc/nginx/nginx.conf
- ./b_th_web/logs:/var/log/nginx
- ./b_th_web/conf.d:/etc/nginx/conf.d
depends_on:
- ruoyi-gateway
links:
- ruoyi-gateway
ruoyi-gateway:
container_name: ruoyi-gateway
image: gateway-runtime
@ -293,3 +311,224 @@ services:
links:
- ruoyi-redis
- ruoyi-mysql
# ============================================================================
# WVP-PRO 视频管理平台
# ============================================================================
# WVP是基于GB28181协议的视频管理平台使用ZLMediaKit作为流媒体服务器
# 端口说明:
# - 18080:18978 → WVP的HTTP API端口外部通过18080访问
# - 5060:5060/udp → SIP信令端口用于GB28181设备注册和控制
# ============================================================================
wvp-pro:
container_name: wvp-pro
image: wvp-pro-runtime
build:
context: ./wvp/wvpjar
dockerfile: dockerfile
environment:
- TZ=Asia/Shanghai
# Redis 配置
- REDIS_HOST=ruoyi-redis
- REDIS_PORT=6379
# MySQL 数据库配置
- DATABASE_HOST=ruoyi-mysql
- DATABASE_PORT=3306
- DATABASE_USER=ylcx
- DATABASE_PASSWORD=Tuoheng@2025
# SIP 配置
- SIP_ShowIP=127.0.0.1
- SIP_Port=5060
- SIP_Domain=3502000000
- SIP_Id=35020000002000000001
- SIP_Password=wvp_sip_password
# ========== ZLM 媒体服务器配置 ==========
# ZLM_HOST: WVP调用ZLM API时使用的地址容器内部服务名
# 用途wvp-pro通过 http://zlmediakit:80/index/api/xxx 调用ZLM的RESTful API
- ZLM_HOST=zlmediakit
# ZLM_HOOK_HOST: ZLM回调WVP时使用的地址容器内部服务名
# 用途zlmediakit通过 http://wvp-pro:18978/index/hook/on_publish 回调WVP
- ZLM_HOOK_HOST=wvp-pro
# ZLM_SERCERT: ZLM的API密钥必须与zlmediakit/config.ini中的api.secret一致
- ZLM_SERCERT=fgVdaI75GcSBPeSBvg8NL7aRrlkCtGPv
# ========== 流媒体地址配置 ==========
# Stream_IP: 生成播放地址时使用的IP宿主机外网IP或域名
# 用途WVP生成的播放地址格式为 http://45.120.103.238:9090/live/123.live.flv
# 对应application.yml中的media.stream-ip配置
- Stream_IP=45.120.103.238
# SDP_IP: WVP在国标信令中使用的IP
# 用途GB28181设备通过此IP与WVP进行媒体流传输
# 对应application.yml中的media.sdp-ip配置
- SDP_IP=45.120.103.238
# ========== 流媒体端口配置(宿主机外部端口) ==========
# 以下端口用于生成客户端播放地址必须与zlmediakit容器的端口映射一致
#
# MediaHttp: HTTP播放端口对应zlmediakit的9090:80映射
# 用途生成HTTP-FLV/HLS/TS/RTC播放地址
# 示例http://45.120.103.238:9090/live/123.live.flv
# 对应application.yml中的media.flv-port和media.ws-flv-port配置
- MediaHttp=9090
# MediaHttps: HTTPS播放端口对应zlmediakit的8443:443映射
# 用途生成HTTPS-FLV/HLS/TS播放地址
# 示例https://45.120.103.238:8443/live/123.live.flv
# 对应application.yml中的media.flv-ssl-port和media.ws-flv-ssl-port配置
- MediaHttps=8443
# MediaRtp: RTP代理端口对应zlmediakit的10000:10000映射
# 用途GB28181设备的RTP流传输
# 对应application.yml中的media.rtp-proxy-port配置
# 对应zlmediakit config.ini中的rtp_proxy.port=10000
- MediaRtp=10000
# MediaRtmp: RTMP推流端口对应zlmediakit的1935:1935映射
# 用途OBS等推流工具推流地址
# 示例rtmp://45.120.103.238:1935/live/123
# 对应application.yml中的media.rtmp-port配置
# 对应zlmediakit config.ini中的rtmp.port=1935
- MediaRtmp=1935
# MediaRtsp: RTSP推流/拉流端口对应zlmediakit的8554:554映射
# 用途RTSP协议的推流和拉流
# 示例rtsp://45.120.103.238:8554/live/123
# 对应application.yml中的media.rtsp-port配置
# 对应zlmediakit config.ini中的rtsp.port=554
- MediaRtsp=8554
# 录像配置
- RecordPushLive=false
- RecordSip=false
ports:
- "18080:18978"
- "5060:5060/udp"
# - "6379:6379"
volumes:
- ./wvp/logs:/home/ruoyi/logs
depends_on:
- ruoyi-redis
- ruoyi-mysql
- zlmediakit
links:
- ruoyi-redis
- ruoyi-mysql
- zlmediakit
restart: unless-stopped
# ============================================================================
# ZLMediaKit 流媒体服务器
# ============================================================================
# ZLMediaKit是高性能的流媒体服务器支持RTMP/RTSP/HLS/HTTP-FLV等多种协议
#
# 端口映射说明(格式:宿主机端口:容器内部端口):
# 1. API访问端口
# - 9090:80 → HTTP API端口WVP通过容器内部的80端口访问ZLM API
# - 8443:443 → HTTPS API端口通常不启用
#
# 2. 客户端播放端口:
# - 9090:80 → HTTP-FLV/HLS/TS/RTC播放客户端通过9090端口播放
# - 8443:443 → HTTPS播放端口
#
# 3. 推流/拉流协议端口:
# - 1935:1935 → RTMP推流端口OBS推流地址rtmp://IP:1935/live/stream
# - 8554:554 → RTSP推流/拉流端口
# - 10000:10000 → RTP代理端口TCP/UDP用于GB28181设备
# - 8000:8000/udp → WebRTC UDP端口
# - 9900:9000/udp → WebRTC UDP端口注意避免与minio 9000端口冲突
#
# 配置文件映射:
# - ./zlmediakit/config.ini → /opt/media/conf/config.ini
# 重要配置项:
# * general.mediaServerId=polaris必须与数据库wvp_media_server表的id字段一致
# * api.secret=fgVdaI75GcSBPeSBvg8NL7aRrlkCtGPv必须与ZLM_SERCERT一致
# * http.port=80容器内部HTTP端口
# * rtmp.port=1935RTMP端口
# * rtsp.port=554RTSP端口
# * rtp_proxy.port=10000RTP代理端口
# ============================================================================
zlmediakit:
container_name: zlmediakit
image: registry.t-aaron.com/zlmediakit/zlmediakit:Release.latest
environment:
- TZ=Asia/Shanghai
ports:
# RTMP推流端口容器内部1935映射到宿主机1935
# OBS推流地址rtmp://45.120.103.238:1935/live/streamId
# 对应config.ini中的rtmp.port=1935
- "1935:1935"
# HTTP端口容器内部80映射到宿主机9090
# 用途1WVP通过http://zlmediakit:80访问ZLM API
# 用途2客户端通过http://45.120.103.238:9090播放HTTP-FLV/HLS/TS
# 对应config.ini中的http.port=80
- "9090:80"
# HTTPS端口容器内部443映射到宿主机8443
# 客户端通过https://45.120.103.238:8443播放
# 对应config.ini中的http.sslport=443
- "8443:443"
# RTSP端口容器内部554映射到宿主机8554
# RTSP地址rtsp://45.120.103.238:8554/live/streamId
# 对应config.ini中的rtsp.port=554
- "8554:554"
# RTP代理端口TCP容器内部10000映射到宿主机10000
# 用于GB28181设备的RTP流传输
# 对应config.ini中的rtp_proxy.port=10000
- "10000:10000"
# RTP代理端口UDP容器内部10000映射到宿主机10000
- "10000:10000/udp"
# WebRTC UDP端口容器内部8000映射到宿主机8000
# 对应config.ini中的rtc.port=8000
- "8000:8000/udp"
# WebRTC UDP端口容器内部9000映射到宿主机9900
# 注意宿主机使用9900避免与minio的9000端口冲突
- "9900:9000/udp"
volumes:
- ./zlmediakit/config.ini:/opt/media/conf/config.ini
restart: unless-stopped
wvp-web:
container_name: wvp-web
image: wvp-web-runtime
build:
context: ./wvp/web
environment:
- TZ=Asia/Shanghai
ports:
- "28181:80"
volumes:
- ./wvp/web/html/dist:/home/ruoyi/projects/wvp-ui
- ./wvp/web/conf/nginx.conf:/etc/nginx/nginx.conf
- ./wvp/web/logs:/var/log/nginx
- ./wvp/web/conf.d:/etc/nginx/conf.d
depends_on:
- wvp-pro
links:
- wvp-pro
pgvector-db:
container_name: hyf-pgvector-db
image: registry.t-aaron.com/pgvector/pgvector:pg16
environment:
POSTGRES_USER: drgraph
POSTGRES_PASSWORD: yingping
POSTGRES_DB: th_agenter
TZ: Asia/Shanghai
ports:
- "5433:5432"
volumes:
- ./hyf_backend/pgdata:/var/lib/postgresql/data
- ./hyf_backend/initdb:/docker-entrypoint-initdb.d
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U drgraph -d th_agenter"]
interval: 5s
timeout: 5s
retries: 5
hyf-backend:
container_name: hyf-backend
image: hyf-backend-runtime
build:
context: ./hyf_backend
dockerfile: dockerfile
environment:
- TZ=Asia/Shanghai
- DATABASE_URL=postgresql+asyncpg://drgraph:yingping@pgvector-db:5432/th_agenter
ports:
- "8800:8000"
volumes:
- ./hyf_backend/data/uploads:/app/data/uploads
- ./hyf_backend/data/chroma:/app/data/chroma
- ./hyf_backend/logs:/app/webIOs/output/logs
depends_on:
pgvector-db:
condition: service_healthy
restart: unless-stopped

View File

@ -0,0 +1,13 @@
FROM registry.t-aaron.com/hyf-backend-base:latest
WORKDIR /app
# 复制项目代码(源码在 src 目录下)
COPY src/ .
# 暴露端口
EXPOSE 8000
# 启动命令:先执行数据库迁移,再创建管理员用户,最后启动 uvicorn
CMD ["sh", "-c", "alembic upgrade head 2>/dev/null || true && python scripts/seed_admin.py 2>/dev/null || true && uvicorn main:app --host 0.0.0.0 --port 8000"]

View File

@ -0,0 +1,5 @@
-- 自动创建 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
-- 验证扩展已安装
SELECT extname, extversion FROM pg_extension WHERE extname = 'vector';

View File

View File

@ -20,15 +20,17 @@ http {
index index.html index.htm;
}
location /prod-api/{
location /prod-api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://ruoyi-gateway:8080/;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# 避免actuator暴露
if ($uri ~ "/actuator") {
return 403;
}
@ -38,4 +40,4 @@ http {
root html;
}
}
}
}

View File

@ -12,4 +12,4 @@ WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/tuoheng-device.jar /home/ruoyi/tuoheng-device.jar
# 启动系统服务
ENTRYPOINT ["java","-jar","tuoheng-device.jar"]
ENTRYPOINT ["java","-jar","tuoheng-device.jar","--spring.flyway.validate-on-migrate=false"]

View File

@ -146,6 +146,11 @@
"key": "cover_state",
"type": "integer",
"value": "${data.cover_state}"
},
{
"key": "drone_charge_state.capacity_percent",
"type": "integer",
"value": "${data.drone_charge_state.capacity_percent}"
}
]
}
@ -187,12 +192,27 @@
{
"key": "distance_limit_status.distance_limit",
"type": "integer",
"value": "${distance_limit_status.distance_limit}"
"value": "${data.distance_limit_status.distance_limit}"
},
{
"key": "battery",
"type": "string",
"value": "${data.battery}"
},
{
"key": "position_state.rtk_number",
"type": "integer",
"value": "${data.position_state.rtk_number}"
},
{
"key": "total_flight_time",
"type": "integer",
"value": "${data.total_flight_time}"
},
{
"key": "position_state.gps_number",
"type": "integer",
"value": "${data.position_state.gps_number}"
}
]
}
@ -341,7 +361,7 @@
}
]
},
"name": "DOCK",
"name": "dajiang",
"id": "28239240-5e44-4fb8-9f66-c29c638903ae",
"logLevel": "INFO",
"enableRemoteLogging": false,

View File

@ -12,8 +12,9 @@ services:
thingsboard-ce:
restart: always
image: "registry.t-aaron.com/thingsboard/tb-node:4.2.1.1"
user: root
ports:
- "18080:8080"
- "28080:8080"
- "7070:7070"
- "1883:1883"
- "8883:8883"

View File

@ -1,5 +1,7 @@
会加入ruoyi的默认网络docker_default
docker-compose down -v
#docker volume rm tb-postgres-data
#docker network create docker_default
#第一次的时候执行这个
docker-compose run --rm -e INSTALL_TB=true -e LOAD_DEMO=true thingsboard-ce
docker-compose up -d

View File

@ -0,0 +1,454 @@
{
"broker": {
"host": "mqtt.t-aaron.com",
"port": 10883,
"version": 5,
"clientId": "ThingsBoard_gateway_tuoheng",
"security": {
"type": "basic",
"username": "admin",
"password": "admin"
},
"maxNumberOfWorkers": 100,
"maxMessageNumberPerWorker": 10
},
"mapping": [
{
"topicFilter": "/topic/v1/airportNest/+/realTime/basic",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpression": "(?<=airportNest/)[^/]+(?=/realTime)",
"deviceNameExpressionSource": "topic",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"attributes": [
{
"key": "airportID",
"type": "string",
"value": "${data.airportID}"
},
{
"key": "mac",
"type": "string",
"value": "${data.mac}"
},
{
"key": "software_version",
"type": "string",
"value": "${data.version.software}"
},
{
"key": "hardware_version",
"type": "string",
"value": "${data.version.hardware}"
},
{
"key": "sender",
"type": "string",
"value": "${sender}"
}
],
"timeseries": [
{
"key": "status",
"type": "string",
"value": "${data.status}"
},
{
"key": "send_timestamp",
"type": "long",
"value": "${send_timestamp}"
}
]
}
},
{
"topicFilter": "/topic/v1/airportNest/+/realTime/data",
"subscriptionQos": 0,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpression": "(?<=airportNest/)[^/]+(?=/realTime)",
"deviceNameExpressionSource": "topic",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"attributes": [],
"timeseries": [
{
"key": "weather_rainfall",
"type": "double",
"value": "${nestWeather.data.rainfall}"
},
{
"key": "weather_windLevel",
"type": "double",
"value": "${nestWeather.data.windLevel}"
},
{
"key": "weather_windDir",
"type": "double",
"value": "${nestWeather.data.windDir}"
},
{
"key": "weather_windSpeed",
"type": "double",
"value": "${nestWeather.data.windSpeed}"
},
{
"key": "weather_rainFlag",
"type": "integer",
"value": "${nestWeather.data.rainFlag}"
},
{
"key": "weather_windAngle",
"type": "double",
"value": "${nestWeather.data.windAngle}"
},
{
"key": "battery_level",
"type": "integer",
"value": "${droneBattery.data.Battery_level}"
},
{
"key": "battery_health",
"type": "integer",
"value": "${droneBattery.data.Battery_health}"
},
{
"key": "battery_totalVoltage",
"type": "double",
"value": "${droneBattery.data.totalVoltage}"
},
{
"key": "battery_cellTemp",
"type": "double",
"value": "${droneBattery.data.cellTemp}"
},
{
"key": "battery_mosTemp",
"type": "double",
"value": "${droneBattery.data.mosTemp}"
},
{
"key": "battery_num_cycles",
"type": "integer",
"value": "${droneBattery.data.num_cycles}"
},
{
"key": "battery_discharge_current",
"type": "double",
"value": "${droneBattery.data.Discharge_current}"
},
{
"key": "battery_bCharging",
"type": "integer",
"value": "${droneBattery.data.bCharging}"
},
{
"key": "nest_innerHum",
"type": "double",
"value": "${nestInnerSensor.data.innerHum}"
},
{
"key": "nest_innerTemp",
"type": "double",
"value": "${nestInnerSensor.data.innerTemp}"
},
{
"key": "charger_current",
"type": "double",
"value": "${nestCharger.data.current}"
},
{
"key": "charger_voltage",
"type": "double",
"value": "${nestCharger.data.voltage}"
},
{
"key": "charger_capacity",
"type": "double",
"value": "${nestCharger.data.capacity}"
},
{
"key": "charger_bCharging",
"type": "integer",
"value": "${nestCharger.data.bCharging}"
}
]
}
},
{
"topicFilter": "/topic/v1/heartbeat/+/message",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpression": "(?<=heartbeat/)[^/]+(?=/message)",
"deviceNameExpressionSource": "topic",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"attributes": [],
"timeseries": [
{
"key": "nest_door_status",
"type": "integer",
"value": "${nestDoor.data.status}"
}
]
}
},
{
"topicFilter": "/topic/v1/airportDrone/+/realTime/data",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpression": "(?<=airportDrone/)[^/]+(?=/realTime)",
"deviceNameExpressionSource": "topic",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"attributes": [
{
"key": "deviceid",
"type": "string",
"value": "${data.deviceid}"
}
],
"timeseries": [
{
"key": "latitude",
"type": "double",
"value": "${data.lat}"
},
{
"key": "longitude",
"type": "double",
"value": "${data.lon}"
},
{
"key": "altitude",
"type": "double",
"value": "${data.alt}"
},
{
"key": "altitude_asl",
"type": "double",
"value": "${data.altasl}"
},
{
"key": "roll",
"type": "double",
"value": "${data.roll}"
},
{
"key": "pitch",
"type": "double",
"value": "${data.pitch}"
},
{
"key": "yaw",
"type": "double",
"value": "${data.yaw}"
},
{
"key": "horizontal_speed",
"type": "double",
"value": "${data.hspeed}"
},
{
"key": "vertical_speed",
"type": "double",
"value": "${data.vspeed}"
},
{
"key": "battery_remain",
"type": "integer",
"value": "${data.battery_remain}"
},
{
"key": "voltage",
"type": "double",
"value": "${data.voltage}"
},
{
"key": "gps_signal",
"type": "integer",
"value": "${data.gpssingal}"
},
{
"key": "sat_count",
"type": "integer",
"value": "${data.satcount}"
},
{
"key": "mode",
"type": "string",
"value": "${data.mode}"
},
{
"key": "armed",
"type": "string",
"value": "${data.armed}"
},
{
"key": "tsingal",
"type": "integer",
"value": "${data.tsingal}"
},
{
"key": "flight_time",
"type": "integer",
"value": "${data.flytime}"
},
{
"key": "mileage",
"type": "double",
"value": "${data.mileage}"
},
{
"key": "distance_to_home",
"type": "double",
"value": "${data.distToHome}"
}
]
}
},
{
"topicFilter": "/topic/v1/airportFly/+/control/data",
"subscriptionQos": 1,
"converter": {
"type": "json",
"deviceInfo": {
"deviceNameExpression": "(?<=airportFly/)[^/]+(?=/control)",
"deviceNameExpressionSource": "topic",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "default"
},
"attributes": [],
"timeseries": [
{
"key": "lifter_status",
"type": "integer",
"value": "${data.lifter}"
},
{
"key": "holder_x_status",
"type": "integer",
"value": "${data.holderX}"
},
{
"key": "holder_y_status",
"type": "integer",
"value": "${data.holderY}"
},
{
"key": "controller_status",
"type": "integer",
"value": "${data.controller}"
},
{
"key": "hatch_status",
"type": "integer",
"value": "${data.hatch}"
},
{
"key": "drone_status",
"type": "integer",
"value": "${data.drone}"
}
]
}
}
],
"requestsMapping": {
"connectRequests": [
{
"topicFilter": "sensor/connect",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
}
},
{
"topicFilter": "sensor/+/connect",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor/)(.*?)(?=/connect)",
"deviceProfileExpressionSource": "constant",
"deviceProfileExpression": "Thermometer"
}
}
],
"disconnectRequests": [
{
"topicFilter": "sensor/disconnect",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}"
}
},
{
"topicFilter": "sensor/+/disconnect",
"deviceInfo": {
"deviceNameExpressionSource": "topic",
"deviceNameExpression": "(?<=sensor/)(.*?)(?=/connect)"
}
}
],
"attributeRequests": [
{
"retain": false,
"topicFilter": "v1/devices/me/attributes/request",
"deviceInfo": {
"deviceNameExpressionSource": "message",
"deviceNameExpression": "${serialNumber}"
},
"attributeNameExpressionSource": "message",
"attributeNameExpression": "${versionAttribute}, ${pduAttribute}",
"topicExpression": "devices/${deviceName}/attrs",
"valueExpression": "${attributeKey}: ${attributeValue}"
}
],
"attributeUpdates": [
{
"retain": true,
"deviceNameFilter": ".*",
"attributeFilter": "firmwareVersion",
"topicExpression": "sensor/${deviceName}/${attributeKey}",
"valueExpression": "{\"${attributeKey}\":\"${attributeValue}\"}"
}
],
"serverSideRpc": [
{
"type": "twoWay",
"deviceNameFilter": ".*",
"methodFilter": "echo",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"responseTopicExpression": "sensor/${deviceName}/response/${methodName}/${requestId}",
"responseTopicQoS": 1,
"responseTimeout": 10000,
"valueExpression": "${params}"
},
{
"type": "oneWay",
"deviceNameFilter": ".*",
"methodFilter": "no-reply",
"requestTopicExpression": "sensor/${deviceName}/request/${methodName}/${requestId}",
"valueExpression": "${params}"
}
]
},
"name": "tuoheng",
"id": "tuoheng-gateway-001",
"logLevel": "INFO",
"enableRemoteLogging": false,
"configVersion": "3.7.8"
}

View File

@ -0,0 +1,69 @@
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
# WVP 后端 API 代理(优先匹配)
location /api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://wvp-pro:18978;
}
# 后端静态资源代理(快照图片等,来自后端服务)
location /snap/ {
proxy_pass http://wvp-pro:18978;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# WebSocket 代理支持
location /ws/ {
proxy_pass http://wvp-pro:18978;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 前端静态资源CSS、JS、图片等
location /static/ {
root /home/ruoyi/projects/wvp-ui;
expires 30d;
access_log off;
}
# 前端页面(最后匹配)
location / {
root /home/ruoyi/projects/wvp-ui;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
# 避免actuator暴露
if ($uri ~ "/actuator") {
return 403;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

15
docker/wvp/web/dockerfile Normal file
View File

@ -0,0 +1,15 @@
# 基础镜像
FROM registry.t-aaron.com/nginx:latest
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi/projects/wvp-ui
# 创建目录
RUN mkdir -p /home/ruoyi/projects/wvp-ui
# 指定路径
WORKDIR /home/ruoyi/projects/wvp-ui
# 复制conf文件到路径
COPY ./conf/nginx.conf /etc/nginx/nginx.conf
# 复制html文件到路径
COPY ./html/dist /home/ruoyi/projects/wvp-ui

View File

@ -0,0 +1 @@
d

View File

@ -0,0 +1,13 @@
# 基础镜像
FROM registry.t-aaron.com/eclipse-temurin:21.0.9_10-jre-alpine-3.23
# author
MAINTAINER ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/wvp-pro-2.7.4.jar /home/ruoyi/wvp-pro-2.7.4.jar
# 启动系统服务
ENTRYPOINT ["java","-jar","wvp-pro-2.7.4.jar"]

View File

View File

@ -0,0 +1,464 @@
#!!!!此配置文件为范例配置文件,意在告诉读者,各个配置项的具体含义和作用,
#!!!!该配置文件在执行cmake时会拷贝至release/${操作系统类型}/${编译类型}(例如release/linux/Debug) 文件夹。
#!!!!该文件夹(release/${操作系统类型}/${编译类型})同时也是可执行程序生成目标路径在执行MediaServer进程时它会默认加载同目录下的config.ini文件作为配置文件
#!!!!你如果修改此范例配置文件(conf/config.ini)并不会被MediaServer进程加载因为MediaServer进程默认加载的是release/${操作系统类型}/${编译类型}/config.ini。
#!!!!当然你每次执行cmake该文件确实会被拷贝至release/${操作系统类型}/${编译类型}/config.ini
#!!!!但是一般建议你直接修改release/${操作系统类型}/${编译类型}/config.ini文件修改此文件一般不起作用,除非你运行MediaServer时使用-c参数指定到此文件。
[api]
#是否调试http api,启用调试后会打印每次http请求的内容和回复
apiDebug=1
#一些比较敏感的http api在访问时需要提供secret否则无权限调用
#如果是通过127.0.0.1访问,那么可以不提供secret
secret=fgVdaI75GcSBPeSBvg8NL7aRrlkCtGPv
#截图保存路径根目录截图通过http api(/index/api/getSnap)生成和获取
snapRoot=./www/snap/
#默认截图图片在启动FFmpeg截图后但是截图还未生成时可以返回默认的预设图片
defaultSnap=./www/logo.png
#downloadFile http接口可访问文件的根目录支持多个目录不同目录通过分号(;)分隔
downloadRoot=./www
[ffmpeg]
#FFmpeg可执行程序路径,支持相对路径/绝对路径
bin=/usr/bin/ffmpeg
#FFmpeg拉流再推流的命令模板通过该模板可以设置再编码的一些参数
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
#FFmpeg生成截图的命令可以通过修改该配置改变截图分辨率或质量
snap=%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s
#FFmpeg日志的路径如果置空则不生成FFmpeg日志
#可以为相对(相对于本可执行程序目录)或绝对路径
log=./ffmpeg/ffmpeg.log
# 自动重启的时间(秒), 默认为0, 也就是不自动重启. 主要是为了避免长时间ffmpeg拉流导致的不同步现象
restart_sec=0
#转协议相关开关如果addStreamProxy api和on_publish hook回复未指定转协议参数则采用这些配置项
[protocol]
#转协议时,是否开启帧级时间戳覆盖
# 0:采用源视频流绝对时间戳,不做任何改变
# 1:采用zlmediakit接收数据时的系统时间戳(有平滑处理)
# 2:采用源视频流时间戳相对时间戳(增长量),有做时间戳跳跃和回退矫正
modify_stamp=2
#转协议是否开启音频
enable_audio=1
#添加acc静音音频在关闭音频时此开关无效
add_mute_audio=1
#无人观看时,是否直接关闭(而不是通过on_none_reader hook返回close)
#此配置置1时此流如果无人观看将不触发on_none_reader hook回调
#而是将直接关闭流
auto_close=0
#推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
#置0关闭此特性(推流断开会导致立即断开播放器)
#此参数不应大于播放器超时时间;单位毫秒
continue_push_ms=3000
#平滑发送定时器间隔单位毫秒置0则关闭开启后影响cpu性能同时增加内存
#该配置开启后可以解决一些流发送不平滑导致zlmediakit转发也不平滑的问题
paced_sender_ms=0
#是否开启转换为hls(mpegts)
enable_hls=1
#是否开启转换为hls(fmp4)
enable_hls_fmp4=0
#是否开启MP4录制
enable_mp4=0
#是否开启转换为rtsp/webrtc
enable_rtsp=1
#是否开启转换为rtmp/flv
enable_rtmp=1
#是否开启转换为http-ts/ws-ts
enable_ts=1
#是否开启转换为http-fmp4/ws-fmp4
enable_fmp4=1
#是否将mp4录制当做观看者
mp4_as_player=0
#mp4切片大小单位秒
mp4_max_second=3600
#mp4录制保存路径
mp4_save_path=/opt/media/bin/www
#hls录制保存路径
hls_save_path=./www
###### 以下是按需转协议的开关在测试ZLMediaKit的接收推流性能时请把下面开关置1
###### 如果某种协议你用不到你可以把以下开关置1以便节省资源(但是还是可以播放,只是第一个播放者体验稍微差点)
###### 如果某种协议你想获取最好的用户体验请置0(第一个播放者可以秒开,且不花屏)
#hls协议是否按需生成如果hls.segNum配置为0(意味着hls录制)那么hls将一直生成(不管此开关)
hls_demand=0
#rtsp[s]协议是否按需生成
rtsp_demand=0
#rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
rtmp_demand=0
#http[s]-ts协议是否按需生成
ts_demand=0
#http[s]-fmp4、ws[s]-fmp4协议是否按需生成
fmp4_demand=0
[general]
#是否启用虚拟主机
enableVhost=0
#播放器或推流器在断开后会触发hook.on_flow_report事件(使用多少流量事件)
#flowThreshold参数控制触发hook.on_flow_report事件阈值使用流量超过该阈值后才触发单位KB
flowThreshold=1024
#播放最多等待时间,单位毫秒
#播放在播放某个流时,如果该流不存在,
#ZLMediaKit会最多让播放器等待maxStreamWaitMS毫秒
#如果在这个时间内,该流注册成功,那么会立即返回播放器播放成功
#否则返回播放器未找到该流,该机制的目的是可以先播放再推流
maxStreamWaitMS=15000
#某个流无人观看时触发hook.on_stream_none_reader事件的最大等待时间单位毫秒
#在配合hook.on_stream_none_reader事件时可以做到无人观看自动停止拉流或停止接收推流
streamNoneReaderDelayMS=20000
#拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
#如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
resetWhenRePlay=1
#合并写缓存大小(单位毫秒)合并写指服务器缓存一定的数据后才会一次性写入socket这样能提高性能但是会提高延时
#开启后会同时关闭TCP_NODELAY并开启MSG_MORE
mergeWriteMS=0
#服务器唯一id用于触发hook时区别是哪台服务器
mediaServerId=polaris
#最多等待未初始化的Track时间单位毫秒超时之后会忽略未初始化的Track
wait_track_ready_ms=10000
#最多等待音频Track收到数据时间单位毫秒超时且完全没收到音频数据忽略音频Track
#加快某些带封装的流metadata说明有音频但是实际上没有的流ready时间比如很多厂商的GB28181 PS
wait_audio_track_data_ms=1000
#如果流只有单Track最多等待若干毫秒超时后未收到其他Track的数据则认为是单Track
#如果协议元数据有声明特定track数那么无此等待时间
wait_add_track_ms=3000
#如果track未就绪我们先缓存帧数据但是有最大个数限制防止内存溢出
unready_frame_cache=100
#是否启用观看人数变化事件广播置1则启用置0则关闭
broadcast_player_count_changed=0
#绑定的本地网卡ip
listen_ip=::
[hls]
#hls写文件的buf大小调整参数可以提高文件io性能
fileBufSize=65536
#hls最大切片时间
segDur=2
#m3u8索引中,hls保留切片个数(实际保留切片个数+segRetain个)
#如果设置为0则不删除切片且m3u8文件全量记录切片列表
segNum=3
#HLS切片延迟个数大于0将生成hls_delay.m3u8文件0则不生成
segDelay=0
#HLS切片从m3u8文件中移除后继续保留在磁盘上的个数
segRetain=5
#是否广播 hls切片(ts/fmp4)完成通知(on_record_ts)
broadcastRecordTs=0
#直播hls文件删除延时单位秒issue: #913
deleteDelaySec=10
#此选项开启后m3u8文件还是表现为直播但是切片文件会被全部保留为点播用
#segDur设置为0或segKeep设置为1的情况下每个切片文件夹下会生成一个vod.m3u8文件用于点播该时间段的录像
segKeep=0
#如果设置为1则第一个切片长度强制设置为1个GOP。当GOP小于segDur可以提高首屏速度
fastRegister=0
[hook]
#是否启用hook事件启用后推拉流都将进行鉴权
enable=1
#播放器或推流器使用流量事件,置空则关闭
on_flow_report=
#访问http文件鉴权事件置空则关闭鉴权
on_http_access=
#播放鉴权事件,置空则关闭鉴权
on_play=http://wvp-pro:18978/index/hook/on_play
#推流鉴权事件,置空则关闭鉴权
on_publish=http://wvp-pro:18978/index/hook/on_publish
#录制mp4切片完成事件
on_record_mp4=http://wvp-pro:18978/index/hook/on_record_mp4
# 录制 hls ts(或fmp4) 切片完成事件
on_record_ts=
#rtsp播放鉴权事件此事件中比对rtsp的用户名密码
on_rtsp_auth=
#rtsp播放是否开启专属鉴权事件置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权
#建议开发者统一采用url参数方式鉴权rtsp用户名密码鉴权一般在设备上用的比较多
#开启rtsp专属鉴权后将不再触发on_play鉴权事件
on_rtsp_realm=
#远程telnet调试鉴权事件
on_shell_login=
#直播流注册或注销事件
on_stream_changed=http://wvp-pro:18978/index/hook/on_stream_changed
#过滤on_stream_changed hook的协议类型可以选择只监听某些感兴趣的协议置空则不过滤协议
stream_changed_schemas=rtsp/rtmp/fmp4/ts/hls/hls.fmp4
#无人观看流事件通过该事件可以选择是否关闭无人观看的流。配合general.streamNoneReaderDelayMS选项一起使用
on_stream_none_reader=http://wvp-pro:18978/index/hook/on_stream_none_reader
#播放时未找到流事件通过配合hook.on_stream_none_reader事件可以完成按需拉流
on_stream_not_found=http://wvp-pro:18978/index/hook/on_stream_not_found
#服务器启动报告,可以用于服务器的崩溃重启事件监听
on_server_started=http://wvp-pro:18978/index/hook/on_server_started
#服务器退出报告,当服务器正常退出时触发
on_server_exited=
#server保活上报
on_server_keepalive=http://wvp-pro:18978/index/hook/on_server_keepalive
#发送rtp(startSendRtp)被动关闭时回调
on_send_rtp_stopped=http://wvp-pro:18978/index/hook/on_send_rtp_stopped
#rtp server 超时未收到数据
on_rtp_server_timeout=http://wvp-pro:18978/index/hook/on_rtp_server_timeout
#hook api最大等待回复时间单位秒
timeoutSec=30
#keepalive hook触发间隔,单位秒float类型
alive_interval=10.0
#hook通知失败重试次数,正整数。为0不重试1时重试一次以此类推
retry=1
#hook通知失败重试延时单位秒float型
retry_delay=3.0
[cluster]
#设置源站拉流url模板, 格式跟printf类似第一个%s指定app,第二个%s指定stream_id,
#开启集群模式后on_stream_not_found和on_stream_none_reader hook将无效.
#溯源模式支持以下类型:
#rtmp方式: rtmp://127.0.0.1:1935/%s/%s
#rtsp方式: rtsp://127.0.0.1:554/%s/%s
#hls方式: http://127.0.0.1:80/%s/%s/hls.m3u8
#http-ts方式: http://127.0.0.1:80/%s/%s.live.ts
#支持多个源站,不同源站通过分号(;)分隔
origin_url=
#溯源总超时时长单位秒float型假如源站有3个那么单次溯源超时时间为timeout_sec除以3
#单次溯源超时时间不要超过general.maxStreamWaitMS配置
timeout_sec=15
#溯源失败尝试次数,-1时永久尝试
retry_count=3
[http]
#http服务器字符编码集
charSet=utf-8
#http链接超时时间
keepAliveSecond=30
#http请求体最大字节数如果post的body太大则不适合缓存body在内存
maxReqSize=40960
#404网页内容用户可以自定义404网页
#notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit-4.0</center></body></html>
#http服务器监听端口
port=80
#http文件服务器根目录
#可以为相对(相对于本可执行程序目录)或绝对路径
rootPath=./www
#http文件服务器读文件缓存大小单位BYTE调整该参数可以优化文件io性能
sendBufSize=65536
#https服务器监听端口
sslport=443
#是否显示文件夹菜单,开启后可以浏览文件夹
dirMenu=1
#虚拟目录, 虚拟目录名和文件路径使用","隔开,多个配置路径间用";"隔开
#例如赋值为 app_a,/path/to/a;app_b,/path/to/b 那么
#访问 http://127.0.0.1/app_a/file_a 对应的文件路径为 /path/to/a/file_a
#访问 http://127.0.0.1/app_b/file_b 对应的文件路径为 /path/to/b/file_b
#访问其他http路径,对应的文件路径还是在rootPath内
virtualPath=
#禁止后缀的文件使用mmap缓存使用“,”隔开
#例如赋值为 .mp4,.flv
#那么访问后缀为.mp4与.flv 的文件不缓存
forbidCacheSuffix=
#可以把http代理前真实客户端ip放在http头中https://github.com/ZLMediaKit/ZLMediaKit/issues/1388
#切勿暴露此key否则可能导致伪造客户端ip
forwarded_ip_header=
#默认允许所有跨域请求
allow_cross_domains=1
#允许访问http api和http文件索引的ip地址范围白名单置空情况下不做限制
allow_ip_range=::1,127.0.0.1,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255
[multicast]
#rtp组播截止组播ip地址
addrMax=239.255.255.255
#rtp组播起始组播ip地址
addrMin=239.0.0.0
#组播udp ttl
udpTTL=64
[record]
#mp4录制或mp4点播的应用名通过限制应用名可以防止随意点播
#点播的文件必须放置在此文件夹下
appName=record
#mp4录制写文件缓存单位BYTE,调整参数可以提高文件io性能
fileBufSize=65536
#mp4点播每次流化数据量单位毫秒
#减少该值可以让点播数据发送量更平滑增大该值则更节省cpu资源
sampleMS=500
#mp4录制完成后是否进行二次关键帧索引写入头部
fastStart=0
#MP4点播(rtsp/rtmp/http-flv/ws-flv)是否循环播放文件
fileRepeat=0
#MP4录制写文件格式是否采用fmp4启用的话断电未完成录制的文件也能正常打开
enableFmp4=0
[rtmp]
#rtmp必须在此时间内完成握手否则服务器会断开链接单位秒
handshakeSecond=15
#rtmp超时时间如果该时间内未收到客户端的数据
#或者tcp发送缓存超过这个时间则会断开连接单位秒
keepAliveSecond=15
#rtmp服务器监听端口
port=1935
#rtmps服务器监听地址
sslport=0
# rtmp是否直接代理模式
directProxy=1
#h265/opus/vp8/vp9/av1 rtmp打包采用增强型rtmp标准还是国内拓展标准
enhanced=1
[rtp]
#音频mtu大小该参数限制rtp最大字节数推荐不要超过1400
#加大该值会明显增加直播延时
audioMtuSize=600
#视频mtu大小该参数限制rtp最大字节数推荐不要超过1400
videoMtuSize=1400
#rtp包最大长度限制单位KB,主要用于识别TCP上下文破坏时获取到错误的rtp
rtpMaxSize=10
# rtp 打包时低延迟开关默认关闭为0h264存在一帧多个sliceNAL的情况在这种情况下如果开启可能会导致画面花屏
lowLatency=0
# H264 rtp打包模式是否采用stap-a模式(为了在老版本浏览器上兼容webrtc)还是采用Single NAL unit packet per H.264 模式
# 有些老的rtsp设备不支持stap-a rtp设置此配置为0可提高兼容性
h264_stap_a=1
[rtp_proxy]
#导出调试数据(包括rtp/ps/h264)至该目录,置空则关闭数据导出
dumpDir=
#udp和tcp代理服务器支持rtp(必须是ts或ps类型)代理
port=10000
#rtp超时时间单位秒
timeoutSec=15
#随机端口范围最少确保36个端口
#该范围同时限制rtsp服务器udp端口范围
port_range=30000-30500
#rtp h264 负载的pt
h264_pt=98
#rtp h265 负载的pt
h265_pt=99
#rtp ps 负载的pt
ps_pt=96
#rtp opus 负载的pt
opus_pt=100
#startSendRtp、startRecord相关功能是否提前开启gop缓存优化级联秒开体验默认开启, 并缓存1个GOP
#如果不调用startSendRtp、startRecord后相关接口可以置0节省内存如果缓存多个gop可以加大该参数
gop_cache=1
#国标发送g711 rtp 打包时每个包的语音时长是多少默认是100 ms范围为20~180ms (gb28181-2016c.2.4规定)
#最好为20 的倍数程序自动向20的倍数取整
rtp_g711_dur_ms=100
#udp接收数据socket buffer大小配置
#4*1024*1024=4196304
udp_recv_socket_buffer=4194304
#ps/ts解析后是否等待下一帧以判断本帧是否完整开启后提高兼容性但是可能增加延时
merge_frame=1
[rtc]
#webrtc 信令服务器端口
signalingPort=3000
signalingSslPort=3001
#STUN/TURN服务器端口
icePort=3478
iceTcpPort=3478
#STUN/TURN端口是否使能TURN服务
enableTurn=1
#ICE传输策略0=不限制(默认)1=仅支持Relay转发2=仅支持P2P直连
iceTransportPolicy=0
#STUN/TURN 服务Ice密码
iceUfrag=ZLMediaKit
icePwd=ZLMediaKit
#webrtc datachannel是否回显数据测试用
datachannel_echo=1
max_stun_retry=7
#TURN服务分配端口池
port_range=49152-65535
#rtc播放推流、播放超时时间
timeoutSec=15
#本机对rtc客户端的可见ip作为服务器时一般为公网ip可有多个用','分开当置空时会自动获取网卡ip
#同时支持环境变量,以$开头,如"$EXTERN_IP"; 请参考https://github.com/ZLMediaKit/ZLMediaKit/pull/1786
externIP=
#当指定了interfaces,ICE服务器会使用指定网卡bind socket
#以解决公网IP使用弹性公网IP配置实现(部署机器无法bind该公网ip的问题)
#支持环境变量,以$开头,如"$PRIVATE_IP"
interfaces=
#rtc udp服务器监听端口号所有rtc客户端将通过该端口传输stun/dtls/srtp/srtcp数据
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
#需要注意的是如果服务器在nat内需要做端口映射时必须确保外网映射端口跟该端口一致
port=8000
#rtc tcp服务器监听端口号在udp 不通的情况下会使用tcp传输数据
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
#需要注意的是如果服务器在nat内需要做端口映射时必须确保外网映射端口跟该端口一致
tcpPort=8000
#设置remb比特率非0时关闭twcc并开启remb。该设置在rtc推流时有效可以控制推流画质
#目前已经实现twcc自动调整码率关闭remb根据真实网络状况调整码率
rembBitRate=0
#rtc支持的音频codec类型,在前面的优先级更高
#以下范例为所有支持的音频codec
preferredCodecA=PCMA,PCMU,opus,mpeg4-generic
#rtc支持的视频codec类型,在前面的优先级更高
#以下范例为所有支持的视频codec
preferredCodecV=H264,H265,AV1,VP9,VP8
#webrtc比特率设置
start_bitrate=0
max_bitrate=0
min_bitrate=0
#nack接收端, rtp发送端zlm发送rtc流
#rtp重发缓存列队最大长度单位毫秒
maxRtpCacheMS=5000
#rtp重发缓存列队最大长度单位个数
maxRtpCacheSize=2048
#nack发送端rtp接收端zlm接收rtc推流
#最大保留的rtp丢包状态个数
nackMaxSize=2048
#rtp丢包状态最长保留时间
nackMaxMS=3000
#nack最多请求重传次数
nackMaxCount=15
#nack重传频率rtt的倍数
nackIntervalRatio=1.0
#视频nack包中rtp个数减小此值可以让nack包响应更灵敏
nackRtpSize=8
#音频nack包中rtp个数减小此值可以让nack包响应更灵敏
nackAudioRtpSize=4
#是否尝试过滤 b帧
bfilter=0
# 是否优先采用webrtc over tcp模式
preferred_tcp=0
[srt]
#srt播放推流、播放超时时间,单位秒
timeoutSec=5
#srt udp服务器监听端口号所有srt客户端将通过该端口传输srt数据
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
port=9000
#srt 协议中延迟缓存的估算参数在握手阶段估算rtt ,然后latencyMul*rtt 为最大缓存时长,此参数越大,表示等待重传的时长就越大
latencyMul=4
#包缓存的大小
pktBufSize=8192
#srt udp服务器的密码,为空表示不加密
passPhrase=
[rtsp]
#rtsp专有鉴权方式是采用base64还是md5方式
authBasic=0
#rtsp拉流、推流代理是否是直接代理模式
#直接代理后支持任意编码格式但是会导致GOP缓存无法定位到I帧可能会导致开播花屏
#并且如果是tcp方式拉流如果rtp大于mtu会导致无法使用udp方式代理
#假定您的拉流源地址不是264或265或AAC那么你可以使用直接代理的方式来支持rtsp代理
#如果你是rtsp推拉流但是webrtc播放也建议关闭直接代理模式
#因为直接代理时rtp中可能没有sps pps,会导致webrtc无法播放; 另外webrtc也不支持Single NAL Unit Packets类型rtp
#默认开启rtsp直接代理rtmp由于没有这些问题是强制开启直接代理的
directProxy=1
#rtsp必须在此时间内完成握手否则服务器会断开链接单位秒
handshakeSecond=15
#rtsp超时时间如果该时间内未收到客户端的数据
#或者tcp发送缓存超过这个时间则会断开连接单位秒
keepAliveSecond=15
#rtsp服务器监听地址
port=554
#rtsps服务器监听地址
sslport=0
#rtsp 转发是否使用低延迟模式当开启时不会缓存rtp包来提高并发可以降低一帧的延迟
lowLatency=0
#强制协商rtp传输方式 (0:TCP,1:UDP,2:MULTICAST,-1:不限制)
#当客户端发起RTSP SETUP的时候如果传输类型和此配置不一致则返回461 Unsupported transport
#迫使客户端重新SETUP并切换到对应协议。目前支持FFMPEG和VLC
rtpTransportType=-1
[shell]
#调试telnet服务器接受最大buffer大小
maxReqSize=1024
#调试telnet服务器监听端口
port=0
# onvif搜索用
[onvif]
port=3702
[general]
check_nvidia_dev=1
enable_ffmpeg_log=0
[http]
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit(git hash:/,branch:,build time:2026-01-14T08:09:34)</center></body></html>

View File

@ -1,20 +0,0 @@
# 此镜像为github持续集成自动编译推送跟代码(master分支)保持最新状态
services:
zlmediakit:
image: registry.t-aaron.com/zlmediakit/zlmediakit:Release.latest
container_name: zlmediakit
restart: unless-stopped
ports:
- "${RTMP_PORT:-1935}:1935" # RTMP
- "${HTTP_PORT:-9090}:80" # HTTP
- "${HTTPS_PORT:-8443}:443" # HTTPS
- "${RTSP_PORT:-8554}:554" # RTSP
- "${RTP_TCP_PORT:-10000}:10000" # RTP TCP
- "${RTP_UDP_PORT:-10000}:10000/udp" # RTP UDP
- "${WEBRTC_UDP_PORT1:-8000}:8000/udp" # WebRTC UDP
- "${WEBRTC_UDP_PORT2:-9000}:9000/udp" # WebRTC UDP
networks:
default:
external: true
name: docker_default

1
hyf-backend Submodule

@ -0,0 +1 @@
Subproject commit 6f0ac0c588c9a1711ddbe833c638206eed4bb3c4

View File

@ -0,0 +1,43 @@
package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.SysDictData;
import com.ruoyi.system.api.factory.RemoteDictFallbackFactory;
import java.util.List;
/**
* 数据字典服务
*
* @author ruoyi
*/
@FeignClient(contextId = "remoteDictService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteDictFallbackFactory.class)
public interface RemoteDictService
{
/**
* 根据字典类型查询字典数据
*
* @param dictType 字典类型
* @param source 请求来源
* @return 结果
*/
@GetMapping("/dict/data/type/{dictType}")
public R<List<SysDictData>> getDictDataByType(@PathVariable("dictType") String dictType, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 根据字典类型和值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @param source 请求来源
* @return 结果
*/
@GetMapping("/dict/data/label/{dictType}/{dictValue}")
public R<String> getDictLabel(@PathVariable("dictType") String dictType, @PathVariable("dictValue") String dictValue, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -12,6 +12,8 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.domain.SysFile;
import com.ruoyi.system.api.factory.RemoteFileFallbackFactory;
import java.io.ByteArrayOutputStream;
/**
* 文件服务
*
@ -37,4 +39,11 @@ public interface RemoteFileService
*/
@DeleteMapping(value = "/delete", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public R<Boolean> delete(@RequestParam("fileUrl") String fileUrl);
/**
* 文件流上传请求
*/
@PostMapping("uploadStream")
public R<String> uploadFileByData(@RequestParam("filename") String filename, @RequestParam("extension") String extension, @RequestParam("data") String data);
}

View File

@ -0,0 +1,45 @@
package com.ruoyi.system.api.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteDictService;
import com.ruoyi.system.api.domain.SysDictData;
import java.util.ArrayList;
import java.util.List;
/**
* 数据字典服务降级处理
*
* @author ruoyi
*/
@Component
public class RemoteDictFallbackFactory implements FallbackFactory<RemoteDictService>
{
private static final Logger log = LoggerFactory.getLogger(RemoteDictFallbackFactory.class);
@Override
public RemoteDictService create(Throwable throwable)
{
log.error("数据字典服务调用失败: {}", throwable.getMessage());
return new RemoteDictService()
{
@Override
public R<List<SysDictData>> getDictDataByType(String dictType, String source)
{
log.error("根据字典类型查询字典数据失败: {}", dictType);
return R.fail("根据字典类型查询字典数据失败");
}
@Override
public R<String> getDictLabel(String dictType, String dictValue, String source)
{
log.error("根据字典类型和值获取字典标签失败: {} - {}", dictType, dictValue);
return R.fail("根据字典类型和值获取字典标签失败");
}
};
}
}

View File

@ -1,41 +1,43 @@
package com.ruoyi.system.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteFileService;
import com.ruoyi.system.api.domain.SysFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.system.api.RemoteFileService;
import com.ruoyi.system.api.domain.SysFile;
import java.io.ByteArrayOutputStream;
/**
* 文件服务降级处理
*
*
* @author ruoyi
*/
@Component
public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileService>
{
public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileService> {
private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class);
@Override
public RemoteFileService create(Throwable throwable)
{
public RemoteFileService create(Throwable throwable) {
log.error("文件服务调用失败:{}", throwable.getMessage());
return new RemoteFileService()
{
return new RemoteFileService() {
@Override
public R<SysFile> upload(MultipartFile file)
{
public R<SysFile> upload(MultipartFile file) {
return R.fail("上传文件失败:" + throwable.getMessage());
}
@Override
public R<Boolean> delete(String fileUrl)
{
public R<Boolean> delete(String fileUrl) {
return R.fail("删除文件失败:" + throwable.getMessage());
}
@Override
public R<String> uploadFileByData(String filename, String extension, String data) {
return R.fail("上传流文件失败:" + throwable.getMessage());
}
};
}
}

View File

@ -1,3 +1,4 @@
com.ruoyi.system.api.factory.RemoteUserFallbackFactory
com.ruoyi.system.api.factory.RemoteLogFallbackFactory
com.ruoyi.system.api.factory.RemoteFileFallbackFactory
com.ruoyi.system.api.factory.RemoteDictFallbackFactory

View File

@ -3,6 +3,7 @@ package com.ruoyi.airline.api;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.airline.api.domain.AirlineFileVO;
import com.ruoyi.airline.api.domain.AirlineTempVO;
import com.ruoyi.airline.api.factory.RemoteAirlineFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
@ -29,4 +30,7 @@ public interface RemoteAirlineService
// TODO
@GetMapping("/airline/{groupId}")
R<AirlineTempVO> getAirlineByGroupId(@PathVariable("groupId") String groupId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@GetMapping("/file/{id}")
R<AirlineFileVO> getFileById(@PathVariable("id") Long id, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -75,4 +75,24 @@ public class AirLinePointVO implements Serializable {
* 转动方向 -1逆时针 1相对机场方向 硬件定义的
*/
private Integer rotateDirection;
/**
* 间隔拍照时间- 命令5000
*/
private Integer photoTime;
/**
* 间隔拍照距离- 命令6000
*/
private Integer photoDistance;
/**
* 照片类型数组1,2,3- 命令5000/6000
*/
private Integer photoType;
/**
* 飞行器偏航角 - 命令4000
*/
private String droneYaw;
}

View File

@ -0,0 +1,36 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 空域分组详情 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineAreaGroupDetailVO extends BaseEntity {
/**
* 分组ID
*/
private Long groupId;
/**
* 空域列表
*/
private List<AirlineAreaVO> airspaceList;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("groupId", getGroupId())
.append("airspaceList", getAirspaceList())
.toString();
}
}

View File

@ -0,0 +1,53 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 空域分组 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineAreaGroupVO extends BaseEntity {
/**
* 用户ID
*/
private Long groupId;
/**
* 分组名称
*/
private String groupName;
/**
* 用户ID分组自带用户归属后期权限都是基于用户ID进行
*/
private Long userId;
/**
* 组关联的空域
*/
private List<AirlineAreaVO> groupInfos;
/**
* 空域数量
*/
private Integer areaCount;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("groupId", getGroupId())
.append("groupName", getGroupName())
.append("userId", getUserId())
.append("areaCount", getAreaCount())
.toString();
}
}

View File

@ -0,0 +1,76 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
/**
* 空域时间规则 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineAreaTimeRuleVO extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 空域ID
*/
private Long areaId;
/**
* 限制类型0是永久 1单次2自定义
*/
private Integer restrictType;
/**
* 日期范围
*/
private Object dateRange;
/**
* 时间范围
*/
private Object timeRange;
/**
* 重复粒度值0,1,2: restrictType=0时生效
*/
private Integer granularity;
/**
* 粒度循环周期整数值每1天每1周每1个月
*/
private Integer repetFrequency;
/**
* 重复粒度具体时间 granularity = 0时该值为空不生效granularity = 1时granularityTimes最多7个值1,2,3,4,5,6,7分别周一周二周日granularity = 2时granularityTimes最多12个值1,2,3,4,5,6,712分别是1月2月.12月
*/
private Object granularityTimes;
/**
* 备注
*/
private String remark;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("areaId", getAreaId())
.append("restrictType", getRestrictType())
.append("dateRange", getDateRange())
.append("timeRange", getTimeRange())
.append("granularity", getGranularity())
.append("repetFrequency", getRepetFrequency())
.append("granularityTimes", getGranularityTimes())
.append("remark", getRemark())
.toString();
}
}

View File

@ -0,0 +1,117 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 空域 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineAreaVO extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 空域名称
*/
private String name;
/**
* 空域类型
*/
private String areaType;
/**
* 1 启用 0 停用默认启用
*/
private Integer status;
/**
* 空域点列表
*/
private List<PointInfo> points;
/**
* 形状
*/
private String shape;
/**
* 面积
*/
private Double areaArea;
/**
* 周长
*/
private Double areaPerimeter;
/**
* 半径
*/
private Double radius;
/**
* 最小高度
*/
private Double minHeight;
/**
* 最大高度
*/
private Double maxHeight;
/**
* 时间规则列表
*/
private List<AirlineAreaTimeRuleVO> timeRules;
/**
* 备注
*/
private String remark;
/**
* 坐标点信息
*/
@Data
public static class PointInfo {
/**
* 纬度
*/
private Double latitude;
/**
* 经度
*/
private Double longitude;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("name", getName())
.append("areaType", getAreaType())
.append("status", getStatus())
.append("points", getPoints())
.append("areaArea", getAreaArea())
.append("areaPerimeter", getAreaPerimeter())
.append("radius", getRadius())
.append("minHeight", getMinHeight())
.append("maxHeight", getMaxHeight())
.append("timeRules", getTimeRules())
.append("remark", getRemark())
.toString();
}
}

View File

@ -18,6 +18,8 @@ public class AirlineFileGroupInfoVO extends BaseEntity {
* id,主键用于order by 排序
*/
private Long id;
private Long airlineId;
/**
* 用户ID
*/

View File

@ -6,6 +6,8 @@ import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 航线分组
*
@ -23,18 +25,22 @@ public class AirlineFileGroupVO extends BaseEntity {
* 分组名称
*/
private String groupName;
/**
* 用户ID分组自带用户归属 后期权限都是基于用户ID进行
*/
private Long userId;
/**
* 组关联的航线
*/
private List<AirlineFileVO> groupInfos;
/**
* 航线数量
*/
private Integer airlineCount;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("groupId", getGroupId())
.append("groupName", getGroupName())
.append("userId", getUserId())
.append("groupId", groupId)
.append("groupName", groupName)
.toString();
}
}

View File

@ -18,7 +18,10 @@ public class AirlineFileVO {
* 主键ID
*/
private Long id;
/**
* 航线名称
*/
private String name;
/**
* 用户ID
*/
@ -33,7 +36,15 @@ public class AirlineFileVO {
* waypoint文件地址
*/
private String fileUrl;
/**
* 飞行器厂商
*/
private String airVendor;
/**
* 飞行器类型
*/
private String airType;
/**
* 原始航线url
@ -42,27 +53,61 @@ public class AirlineFileVO {
/**
* 航线类型1,航点航线;2,指点航线;3,指面航线
* 航线类型waypoint,航点航线;指点航线;指面航线
*/
private Integer type;
private String type;
/**
* 航线点列表
*/
private List<AirLinePointVO> linePointDtoList;
private List<AirLinePointVO> linePointVOList;
/**
* 1 启用 0 停用
*/
private Integer status;
/**
* 爬升模式
*/
private String climbMode;
/**
* 海拔高度
*/
private Double altitude;
/**
* 飞行速度
*/
private Double flightSpeed;
/**
* 全局航点高度
*/
private Double globalWaypointHeight;
/**
* 拍照模式
*/
private String photoMode;
/**
* kmz航线的全局高度
*/
private Integer djiRthAltitude;
/**
* 分组ID
*/
private Long groupId;
/**
* 分组名称
*/
private String groupName;

View File

@ -0,0 +1,36 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 空域分组详情 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineMarkerGroupDetailVO extends BaseEntity {
/**
* 分组ID
*/
private Long groupId;
/**
* 空域列表
*/
private List<AirlineMarkerVO> airspaceList;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("groupId", getGroupId())
.append("airspaceList", getAirspaceList())
.toString();
}
}

View File

@ -0,0 +1,47 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 标注分组 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineMarkerGroupVO extends BaseEntity {
/**
* 分组ID
*/
private Long id;
/**
* 分组名称
*/
private String name;
/**
* 组关联的标注
*/
private List<AirlineMarkerVO> groupInfos;
/**
* 标注数量
*/
private Integer markerCount;
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("name", getName())
.append("markerCount", getMarkerCount())
.toString();
}
}

View File

@ -0,0 +1,102 @@
package com.ruoyi.airline.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
/**
* 标注 VO
*
* @author 拓恒
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirlineMarkerVO extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 标注名称
*/
private String markerName;
/**
* 标注类型
*/
private String markerType;
/**
* 1 启用 0 停用默认启用
*/
private Integer status;
/**
* 颜色
*/
private String color;
/**
* 图标
*/
private String icon;
/**
* 字体大小
*/
private Integer fontSize;
/**
* 经纬度格式[,,asl高度]
*/
private List<PointInfo> coordinates;
/**
* 简介
*/
private String description;
/**
* 分组ID
*/
private Long groupId;
@Data
public static class PointInfo {
/**
* 纬度
*/
private Double latitude;
/**
* 经度
*/
private Double longitude;
/**
* 海拔高度
*/
private Double asl;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("markerName", getMarkerName())
.append("markerType", getMarkerType())
.append("status", getStatus())
.append("color", getColor())
.append("icon", getIcon())
.append("fontSize", getFontSize())
.append("coordinates", getCoordinates())
.append("description", getDescription())
.append("groupId", getGroupId())
.toString();
}
}

View File

@ -1,6 +1,8 @@
package com.ruoyi.airline.api.factory;
import com.ruoyi.airline.api.RemoteAirlineService;
import com.ruoyi.airline.api.domain.AirlineFileVO;
import com.ruoyi.airline.api.domain.AirlineTempVO;
import com.ruoyi.common.core.domain.R;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -22,6 +24,16 @@ public class RemoteAirlineFallbackFactory implements FallbackFactory<RemoteAirli
public RemoteAirlineService create(Throwable throwable)
{
log.error("航线服务调用失败:{}", throwable.getMessage());
return (id, source) -> R.fail("获取航线信息失败:" + throwable.getMessage());
return new RemoteAirlineService() {
@Override
public R<AirlineTempVO> getAirlineByGroupId(String groupId, String source) {
return null;
}
@Override
public R<AirlineFileVO> getFileById(Long id, String source) {
return null;
}
};
}
}

View File

@ -0,0 +1 @@
com.ruoyi.airline.api.factory.RemoteAirlineFallbackFactory

View File

@ -0,0 +1,116 @@
package com.ruoyi.device.api;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.device.api.domain.DroneCurrentStatusVO;
import com.ruoyi.device.api.domain.DroneFlightControlRequest;
import com.ruoyi.device.api.domain.DroneRealtimeInfoVO;
import com.ruoyi.device.api.domain.DroneTakeoffResponseVO;
import com.ruoyi.device.api.domain.MachineStateVO;
import com.ruoyi.device.api.factory.RemoteAircraftFlyFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
/**
* 无人机飞控服务
*
* @author ruoyi
* @date 2026-02-04
*/
@FeignClient(contextId = "remoteAircraftFlyService", value = ServiceNameConstants.DEVICE_SERVICE, fallbackFactory = RemoteAircraftFlyFallbackFactory.class)
public interface RemoteAircraftFlyService
{
/**
* 无人机飞控命令
*
* @param request 飞控命令请求
* @param source 请求来源
* @return 结果
*/
@PostMapping("/drone/flight-control")
R<Void> flightControl(@RequestBody DroneFlightControlRequest request, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 无人机实时信息展示
*
* @param taskId 任务ID
* @param source 请求来源
* @return 实时信息
*/
@GetMapping("/drone/realtime-info/{taskId}")
R<DroneRealtimeInfoVO> getRealtimeInfo(@PathVariable("taskId") Long taskId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 无人机当前状态查询
*
* @param dockId 机场ID
* @param source 请求来源
* @return 当前状态
*/
@GetMapping("/drone/current-status/{dockId}")
R<DroneCurrentStatusVO> getCurrentStatus(@PathVariable("dockId") Long dockId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 无人机起飞接口
*
* @param sn 机场SN号
* @return 起飞响应
*/
@PostMapping("/drone/takeoff/{sn}")
R<String> takeoff(@PathVariable("sn") String sn);
/**
* 无人机开机接口
*
* @param sn 机场SN号
* @return 开机响应
*/
@PostMapping("/drone/power-on/{sn}")
R<String> powerOn(@PathVariable("sn") String sn);
/**
* 无人机关机接口
*
* @param sn 机场SN号
* @return 关机响应
*/
@PostMapping("/drone/power-off/{sn}")
R<String> powerOff(@PathVariable("sn") String sn);
/**
* 查询设备状态
*
* @param sn 设备SN号
* @return 设备状态信息
*/
@GetMapping("/drone/machine-state/{sn}")
R<MachineStateVO> getMachineState(@PathVariable("sn") String sn);
/**
* 出舱接口
*
* @param sn 机场SN号
* @return 出舱响应
*/
@PostMapping("/drone/cover-open/{sn}")
R<String> coverOpen(@PathVariable("sn") String sn);
/**
* 回舱接口
*
* @param sn 机场SN号
* @return 回舱响应
*/
@PostMapping("/drone/cover-close/{sn}")
R<String> coverClose(@PathVariable("sn") String sn);
/**
* 无人机返航接口
*
* @param sn 机场SN号
* @return 返航响应
*/
@PostMapping("/drone/return-home/{sn}")
R<String> returnHome(@PathVariable("sn") String sn);
}

View File

@ -4,10 +4,13 @@ import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.device.api.domain.AircraftDetailVO;
import com.ruoyi.device.api.domain.DockAircraftVO;
import com.ruoyi.device.api.factory.RemoteAircraftFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 无人机服务
*
@ -26,4 +29,13 @@ public interface RemoteAircraftService
*/
@GetMapping("/aircraft/detail/{aircraftId}")
R<AircraftDetailVO> getAircraftDetail(@PathVariable("aircraftId") Long aircraftId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 获取所有机场和机场的无人机
*
* @param source 请求来源
* @return 结果
*/
@GetMapping("/aircraft/dock-aircraft-list")
R<List<DockAircraftVO>> getDockAircraftList(@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -0,0 +1,28 @@
package com.ruoyi.device.api.domain;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机负载类型VO
*
* @author 拓恒
* @date 2026-03-04
*/
@Data
public class AirLoadTypeVO implements Serializable {
private static final long serialVersionUID = 1L;
/** 负载名称 */
private String loadName;
/** 负载系列 */
private String loadSeries;
/** 负载分类0-负载1-配件 */
private Integer loadCategory;
/** 槽1、2 或者-1-1代表全部槽位可用 */
private Integer slot;
}

View File

@ -0,0 +1,31 @@
package com.ruoyi.device.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 无人机分类分组VO
*
* @author ruoyi
* @date 2026-02-09
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirTypeCategoryGroupVO extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 分类标签
*/
private String category;
/**
* 无人机类型列表
*/
private List<AirTypeGeneralEnumVO> airTypeList;
}

View File

@ -0,0 +1,69 @@
package com.ruoyi.device.api.domain;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* 无人机类型通用枚举VO
* Controller 层输入参数
*
* @author ruoyi
* @date 2026-01-28
*/
@Data
public class AirTypeGeneralEnumVO implements Serializable
{
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id;
/** 名称 */
private String name;
/** 厂商ID */
private Long vendorId;
/** 领域 */
private Long domain;
/** 主类型 */
private Long type;
/** 子类型 */
private Long subType;
/** 类型编码domain-type-subType */
private String airType;
/** 图标 */
private String icon;
/** 分类 */
private String category;
/** 是否生效0-失效1-生效 */
private Integer enabled;
/** 槽位数 */
private Integer slotCount;
/** 负载数量限制 */
private Integer loadLimit;
/** 配件限制数量 */
private Integer accessoryLimit;
/** 可用负载列表(按系列分组) */
private Map<String, List<AirLoadTypeVO>> loadList;
/**
* 生成类型编码domain-type-subType
*/
public void generateTypeCode() {
this.airType = String.format("%d-%d-%d", domain, type, subType);
}
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.device.api.domain;
import com.ruoyi.common.core.web.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 无人机厂商分组VO
*
* @author ruoyi
* @date 2026-01-29
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AirTypeVendorGroupVO extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 厂商标签
*/
private String label;
/**
* 厂商值
*/
private String value;
/**
* 无人机分类分组列表
*/
private List<AirTypeCategoryGroupVO> categoryGroups;
/**
* 无人机类型列表
*/
private List<AirTypeGeneralEnumVO> airTypeList;
}

View File

@ -69,7 +69,11 @@ public class AircraftDetailVO extends AircraftVO {
/** RTK信号 */
@Schema(description = "RTK信号")
@Excel(name = "RTK信号")
private Double rtkSignal;
private Integer rtkSignal;
@Schema(description = "GPS信号")
@Excel(name = "GPS信号")
private Integer gpsSignal;
/** 限高 */
@Schema(description = "限高")

View File

@ -0,0 +1,43 @@
package com.ruoyi.device.api.domain;
/**
* 机场无人机VO
*
* @author ruoyi
* @date 2026-03-13
*/
public class DockAircraftVO {
/** 无人机设备SN */
private String deviceSn;
/** 无人机名称 */
private String aircraftName;
/** 机场名称 */
private String dockName;
public String getDeviceSn() {
return deviceSn;
}
public void setDeviceSn(String deviceSn) {
this.deviceSn = deviceSn;
}
public String getAircraftName() {
return aircraftName;
}
public void setAircraftName(String aircraftName) {
this.aircraftName = aircraftName;
}
public String getDockName() {
return dockName;
}
public void setDockName(String dockName) {
this.dockName = dockName;
}
}

View File

@ -147,9 +147,7 @@ public class DockDetailVO extends DockVO {
/**
* 机场状态
*/
@Schema(description = "机场运行状态: IDLE-空闲中, Debugging-现场调试/远程调试, Upgrading-固件升级中, Working-作业中, UNKNOWN-待标定, OFFLINE-离线",
allowableValues = {"IDLE", "Debugging", "Upgrading", "Working", "UNKNOWN", "OFFLINE"})
@Excel(name = "机场运行状态")
@Excel(name = "机场是否正常")
private String dockRunStatus;
/**
@ -169,10 +167,18 @@ public class DockDetailVO extends DockVO {
/**
* 充放电状态
*/
@Schema(description = "充放电状态")
@Schema(description = "充放电状态 空闲: FREE 充电中: CHARGING")
@Excel(name = "充放电状态")
private String chargingStatus;
/**
* 电量百分比
*/
@Schema(description = "电量百分比")
@Excel(name = "电量百分比")
private Integer capacity_percent;
/**
* 舱内温度
*/

View File

@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
@ -91,4 +92,9 @@ public class DockVO implements Serializable {
@Schema(description = "挂载列表")
private List<PayloadVO> payloadList;
/** 最后活跃时间 */
@Schema(description = "最后活跃时间")
@Excel(name = "最后活跃时间", dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date lastActiveTime;
}

View File

@ -82,4 +82,24 @@ public class DockWithGPSVO implements Serializable {
@Schema(description = "经度")
private Double longitude;
/** 电量 */
@Schema(description = "电量百分比")
private Integer capacity_percent;
/** 充放电状态 */
@Schema(description = "充放电状态")
private String chargingStatus;
@Schema(description = "无人机SN号")
private String snNumber;
@Schema(description = "舱内视频地址")
private String cabinVideoUrl;
@Schema(description = "舱外视频地址")
private String outsideVideoUrl;
@Schema(description = "直播视频地址")
private String liveVideoUrl;
}

View File

@ -0,0 +1,30 @@
package com.ruoyi.device.api.domain;
import com.ruoyi.device.api.enums.DroneCurrentStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机当前状态查询对象
*
* @author ruoyi
* @date 2026-02-04
*/
@Data
@Schema(description = "无人机当前状态查询对象")
public class DroneCurrentStatusVO implements Serializable {
private static final long serialVersionUID = 1L;
/** 机场ID */
@Schema(description = "机场ID", example = "1")
private Long dockId;
/** 当前状态 */
@Schema(description = "当前状态: TAKEOFF_FAILED-起飞失败, FLYING-飞行中, POINTING-指点中, HOVERING-悬停中, RETURNING-返航中, LANDING-降落中",
allowableValues = {"TAKEOFF_FAILED", "FLYING", "POINTING", "HOVERING", "RETURNING", "LANDING"},
example = "HOVERING")
private DroneCurrentStatusEnum currentStatus;
}

View File

@ -0,0 +1,78 @@
package com.ruoyi.device.api.domain;
import com.ruoyi.device.api.enums.DroneCommandEnum;
import com.ruoyi.device.api.enums.DroneSpeedEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机飞控命令请求对象
*
* @author ruoyi
* @date 2026-02-04
*/
@Data
@Schema(description = "无人机飞控命令请求对象")
public class DroneFlightControlRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 机场SN号 */
@Schema(description = "机场SN号", example = "THJSQ03B2309DN7VQN43")
private String sn;
/** 任务ID */
@Schema(description = "任务ID", example = "1")
private Long taskId;
/** 飞控命令 */
@Schema(description = "飞控命令: FORWARD-前进, BACKWARD-后退, LEFT-左移, RIGHT-右移, ROTATE_LEFT-左旋, ROTATE_RIGHT-右旋, UP-上升, DOWN-下降, RETURN_HOME-返航, EMERGENCY_STOP-急停, AIRLINE_FLIGHT-航线飞行, HOVER-悬停, CONTINUE_TASK-继续任务",
allowableValues = {"FORWARD", "BACKWARD", "LEFT", "RIGHT", "ROTATE_LEFT", "ROTATE_RIGHT", "UP", "DOWN", "RETURN_HOME", "EMERGENCY_STOP", "AIRLINE_FLIGHT", "HOVER", "CONTINUE_TASK"},
example = "FORWARD")
private DroneCommandEnum command;
/** 速度调整:快、默认、慢 */
@Schema(description = "速度调整: FAST-快, NORMAL-默认, SLOW-慢",
allowableValues = {"FAST", "NORMAL", "SLOW"},
example = "NORMAL")
private DroneSpeedEnum speed;
/** 幅度调整:数值类型 */
@Schema(description = "幅度调整(数值类型,单位:米)", example = "10")
private Integer amplitude;
/** 消息ID */
@Schema(description = "消息ID", example = "9056")
private Long messageID;
/** 扩展值(用于云台控制等) */
@Schema(description = "扩展值(用于云台控制等)", example = "90")
private String evalue;
/** 值(用于云台控制等) */
@Schema(description = "值(用于云台控制等)", example = "04")
private String value;
//专门用于云台变焦命令的用于指定相机的灯光模式
/** 灯光模式(用于相机控制) */
@Schema(description = "灯光模式(用于相机控制)", example = "visibleLight")
private String lightMode;
/** 航线文件URL用于航线飞行 */
@Schema(description = "航线文件URL用于航线飞行", example = "https://minio-jndsj.t-aaron.com:2443/th-airport/testFile/55e8cc5b-d145-43ff-9386-8daabcb5b816.waypoints")
private String airlineFileUrl;
/** 最低飞行电池电量(用于航线飞行) */
@Schema(description = "最低飞行电池电量(用于航线飞行)", example = "0.3")
private Double flyBatteryMin;
/** 是否必须飞行(用于航线飞行) */
@Schema(description = "是否必须飞行(用于航线飞行)", example = "0")
private Integer isMustFly;
/** 指令类型(用于悬停和继续任务) */
@Schema(description = "指令类型(用于悬停和继续任务)", example = "01")
private String zhilin;
}

View File

@ -0,0 +1,58 @@
package com.ruoyi.device.api.domain;
import com.ruoyi.device.api.enums.DroneMissionStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机实时信息展示对象
*
* @author ruoyi
* @date 2026-02-04
*/
@Data
@Schema(description = "无人机实时信息展示对象")
public class DroneRealtimeInfoVO implements Serializable {
private static final long serialVersionUID = 1L;
/** 爬升速度 */
@Schema(description = "爬升速度单位m/s", example = "5")
private Integer climbSpeed;
/** 平飞速度 */
@Schema(description = "平飞速度单位m/s", example = "15")
private Integer cruiseSpeed;
/** 距离机场 */
@Schema(description = "距离机场(单位:米)", example = "1000")
private Integer distanceToAirport;
/** 无人机高度 */
@Schema(description = "无人机高度(单位:米)", example = "100")
private Integer altitude;
/** 无人机俯仰角 */
@Schema(description = "无人机俯仰角(单位:度,范围:-90~90", example = "0")
private Integer pitch;
/** 无人机偏航角 */
@Schema(description = "无人机偏航角单位范围0~360", example = "180")
private Integer yaw;
/** 云台俯仰角 */
@Schema(description = "云台俯仰角(单位:度,范围:-90~90", example = "-45")
private Integer gimbalPitch;
/** 云台偏航角 */
@Schema(description = "云台偏航角单位范围0~360", example = "90")
private Integer gimbalYaw;
/** 无人机任务状态 */
@Schema(description = "无人机任务状态: IDLE-空闲, SELF_CHECKING-自检中, SELF_CHECK_FAILED-自检失败, TAKING_OFF-起飞中, TAKEOFF_FAILED-起飞失败, FLYING-飞行中, POINTING-指点中, HOVERING-悬停中, RETURNING-返航中, LANDING-降落中",
allowableValues = {"IDLE", "SELF_CHECKING", "SELF_CHECK_FAILED", "TAKING_OFF", "TAKEOFF_FAILED", "FLYING", "POINTING", "HOVERING", "RETURNING", "LANDING"},
example = "FLYING")
private DroneMissionStatusEnum missionStatus;
}

View File

@ -0,0 +1,35 @@
package com.ruoyi.device.api.domain;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机返航请求对象
*
* @author ruoyi
* @date 2026-02-27
*/
@Data
@Schema(description = "无人机返航请求对象")
public class DroneReturnHomeRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 机场SN号 */
@Schema(description = "机场SN号", example = "THJSQ03B2309DN7VQN43")
private String sn;
//
// /** 消息ID */
// @Schema(description = "消息ID", example = "9056")
// private Long messageID;
//
// /** 任务ID */
// @Schema(description = "任务ID", example = "9074")
// private Long taskId;
// /** 返航类型 */
// @Schema(description = "返航类型", example = "03")
// private String zhilin;
}

View File

@ -0,0 +1,40 @@
package com.ruoyi.device.api.domain;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机起飞请求对象
*
* @author ruoyi
* @date 2026-02-27
*/
@Data
@Schema(description = "无人机起飞请求对象")
public class DroneTakeoffRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 机场SN号 */
@Schema(description = "机场SN号", example = "THJSQ03B2309DN7VQN43")
private String sn;
//
// /** 消息ID */
// @Schema(description = "消息ID", example = "9056")
// private Long messageID;
// /** 航线文件URL */
// @Schema(description = "航线文件URL", example = "https://minio-jndsj.t-aaron.com:2443/th-airport/testFile/13912c62-b96f-4df5-ab65-813c8c4b04eb.waypoints")
// private String airlineFileUrl;
/** 最低电池电量 */
@Schema(description = "最低电池电量", example = "0.3")
private Double flyBatteryMin;
/**
* 任务ID
*/
private Long taskId;
}

View File

@ -0,0 +1,30 @@
package com.ruoyi.device.api.domain;
import com.ruoyi.device.api.enums.DroneMissionStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 无人机起飞响应对象
*
* @author ruoyi
* @date 2026-02-04
*/
@Data
@Schema(description = "无人机起飞响应对象")
public class DroneTakeoffResponseVO implements Serializable {
private static final long serialVersionUID = 1L;
/** 任务ID */
@Schema(description = "任务ID", example = "1001")
private Long taskId;
/** 无人机任务状态 */
@Schema(description = "无人机任务状态: IDLE-空闲, SELF_CHECKING-自检中, SELF_CHECK_FAILED-自检失败, TAKING_OFF-起飞中, TAKEOFF_FAILED-起飞失败, FLYING-飞行中, POINTING-指点中, HOVERING-悬停中, RETURNING-返航中, LANDING-降落中",
allowableValues = {"IDLE", "SELF_CHECKING", "SELF_CHECK_FAILED", "TAKING_OFF", "TAKEOFF_FAILED", "FLYING", "POINTING", "HOVERING", "RETURNING", "LANDING"},
example = "TAKING_OFF")
private DroneMissionStatusEnum missionStatus;
}

View File

@ -0,0 +1,106 @@
package com.ruoyi.device.api.domain;
import java.io.Serializable;
/**
* 设备状态视图对象
* API VO用于前后端数据交互
*
* @author ruoyi
* @date 2026-02-10
*/
public class MachineStateVO implements Serializable
{
private static final long serialVersionUID = 1L;
/** 设备SN号 */
private String sn;
/** 无人机状态 */
private String droneState;
/** 机场状态 */
private String airportState;
/** 舱门状态 */
private String coverState;
/** DRC状态 */
private String drcState;
/** 调试模式状态 */
private String debugModeState;
public String getSn()
{
return sn;
}
public void setSn(String sn)
{
this.sn = sn;
}
public String getDroneState()
{
return droneState;
}
public void setDroneState(String droneState)
{
this.droneState = droneState;
}
public String getAirportState()
{
return airportState;
}
public void setAirportState(String airportState)
{
this.airportState = airportState;
}
public String getCoverState()
{
return coverState;
}
public void setCoverState(String coverState)
{
this.coverState = coverState;
}
public String getDrcState()
{
return drcState;
}
public void setDrcState(String drcState)
{
this.drcState = drcState;
}
public String getDebugModeState()
{
return debugModeState;
}
public void setDebugModeState(String debugModeState)
{
this.debugModeState = debugModeState;
}
@Override
public String toString()
{
return "MachineStateVO{" +
"sn='" + sn + '\'' +
", droneState='" + droneState + '\'' +
", airportState='" + airportState + '\'' +
", coverState='" + coverState + '\'' +
", drcState='" + drcState + '\'' +
", debugModeState='" + debugModeState + '\'' +
'}';
}
}

View File

@ -3,6 +3,8 @@ package com.ruoyi.device.api.domain;
import com.ruoyi.common.core.annotation.Excel;
import lombok.Data;
import java.util.List;
/**
* 切换机场分组请求对象
*
@ -12,9 +14,12 @@ import lombok.Data;
@Data
public class SwitchDockGroupRequest
{
@Excel(name = "机场ID")
@Excel(name = "机场ID 列表")
private Long dockId;
@Excel(name = "机场ID 列表")
private List<Long> dockIds;
@Excel(name = "分组ID")
private Long groupId;

View File

@ -16,17 +16,17 @@ public enum DockStatusEnum {
/**
* 现场调试/远程调试
*/
Debugging("Debugging", "现场调试/远程调试"),
Debugging("DEBUGGING", "现场调试/远程调试"),
/**
* 固件升级中
*/
FIRMWARE_UPGRADING("Upgrading", "固件升级中"),
FIRMWARE_UPGRADING("UPGRADING", "固件升级中"),
/**
* 作业中
*/
WORKING("Working", "作业中"),
WORKING("WORKING", "作业中"),
/**
* 待标定/未知

View File

@ -0,0 +1,141 @@
package com.ruoyi.device.api.enums;
/**
* 无人机飞控命令枚举
*
* @author ruoyi
* @date 2026-02-04
*/
public enum DroneCommandEnum {
/**
* 前进
*/
FORWARD("FORWARD", "前进"),
/**
* 后退
*/
BACKWARD("BACKWARD", "后退"),
/**
* 左移
*/
LEFT("LEFT", "左移"),
/**
* 右移
*/
RIGHT("RIGHT", "右移"),
/**
* 左旋
*/
ROTATE_LEFT("ROTATE_LEFT", "左旋"),
/**
* 右旋
*/
ROTATE_RIGHT("ROTATE_RIGHT", "右旋"),
/**
* 上升
*/
UP("UP", "上升"),
/**
* 下降
*/
DOWN("DOWN", "下降"),
// /**
// * 返航
// */
// RETURN_HOME("RETURN_HOME", "返航"),
/**
* 急停
*/
EMERGENCY_STOP("EMERGENCY_STOP", "急停"),
/**
* 切换可见光
*/
SWITCH_VISIBLE_LIGHT("SWITCH_VISIBLE_LIGHT", "切换可见光"),
/**
* 云台变焦
*/
GIMBAL_ZOOM("GIMBAL_ZOOM", "云台变焦"),
/**
* 切换红外
*/
SWITCH_IR("SWITCH_IR", "切换红外"),
/**
* 切换广角
*/
SWITCH_WIDE_ANGLE("SWITCH_WIDE_ANGLE", "切换广角"),
/**
* 云台右移
*/
GIMBAL_MOVE_RIGHT("GIMBAL_MOVE_RIGHT", "云台右移"),
/**
* 云台左移
*/
GIMBAL_MOVE_LEFT("GIMBAL_MOVE_LEFT", "云台左移"),
/**
* 云台俯仰
*/
GIMBAL_PITCH_UP("GIMBAL_PITCH_UP", "云台俯仰(上)"),
/**
* 云台俯仰
*/
GIMBAL_PITCH_DOWN("GIMBAL_PITCH_DOWN", "云台俯仰(下)"),
/**
* 云台复位
*/
GIMBAL_RESET("GIMBAL_RESET", "云台复位"),
/**
* 航线飞行
*/
AIRLINE_FLIGHT("AIRLINE_FLIGHT", "航线飞行"),
/**
* 悬停
*/
HOVER("HOVER", "悬停"),
/**
* 继续任务
*/
CONTINUE_TASK("CONTINUE_TASK", "继续任务");
private final String code;
private final String description;
DroneCommandEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return code;
}
}

View File

@ -0,0 +1,61 @@
package com.ruoyi.device.api.enums;
/**
* 无人机当前状态枚举
*
* @author ruoyi
* @date 2026-02-04
*/
public enum DroneCurrentStatusEnum {
/**
* 起飞失败
*/
TAKEOFF_FAILED("TAKEOFF_FAILED", "起飞失败"),
/**
* 飞行中
*/
FLYING("FLYING", "飞行中"),
/**
* 指点中
*/
POINTING("POINTING", "指点中"),
/**
* 悬停中
*/
HOVERING("HOVERING", "悬停中"),
/**
* 返航中
*/
RETURNING("RETURNING", "返航中"),
/**
* 降落中
*/
LANDING("LANDING", "降落中");
private final String code;
private final String description;
DroneCurrentStatusEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return code;
}
}

View File

@ -0,0 +1,81 @@
package com.ruoyi.device.api.enums;
/**
* 无人机任务状态枚举
*
* @author ruoyi
* @date 2026-02-04
*/
public enum DroneMissionStatusEnum {
/**
* 空闲
*/
IDLE("IDLE", "空闲"),
/**
* 自检中
*/
SELF_CHECKING("SELF_CHECKING", "自检中"),
/**
* 自检失败
*/
SELF_CHECK_FAILED("SELF_CHECK_FAILED", "自检失败"),
/**
* 起飞中
*/
TAKING_OFF("TAKING_OFF", "起飞中"),
/**
* 起飞失败
*/
TAKEOFF_FAILED("TAKEOFF_FAILED", "起飞失败"),
/**
* 飞行中
*/
FLYING("FLYING", "飞行中"),
/**
* 指点中
*/
POINTING("POINTING", "指点中"),
/**
* 悬停中
*/
HOVERING("HOVERING", "悬停中"),
/**
* 返航中
*/
RETURNING("RETURNING", "返航中"),
/**
* 降落中
*/
LANDING("LANDING", "降落中");
private final String code;
private final String description;
DroneMissionStatusEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return code;
}
}

View File

@ -0,0 +1,46 @@
package com.ruoyi.device.api.enums;
/**
* 无人机速度调整枚举
*
* @author ruoyi
* @date 2026-02-04
*/
public enum DroneSpeedEnum {
/**
* 快速
*/
FAST("FAST", ""),
/**
* 默认
*/
NORMAL("NORMAL", "默认"),
/**
* 慢速
*/
SLOW("SLOW", "");
private final String code;
private final String description;
DroneSpeedEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return code;
}
}

View File

@ -3,11 +3,14 @@ package com.ruoyi.device.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.device.api.RemoteAircraftService;
import com.ruoyi.device.api.domain.AircraftDetailVO;
import com.ruoyi.device.api.domain.DockAircraftVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 无人机服务降级处理
*
@ -30,6 +33,12 @@ public class RemoteAircraftFallbackFactory implements FallbackFactory<RemoteAirc
{
return R.fail("查看无人机详情失败:" + throwable.getMessage());
}
@Override
public R<List<DockAircraftVO>> getDockAircraftList(String source)
{
return R.fail("获取机场无人机列表失败:" + throwable.getMessage());
}
};
}
}

View File

@ -0,0 +1,93 @@
package com.ruoyi.device.api.factory;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.device.api.RemoteAircraftFlyService;
import com.ruoyi.device.api.domain.DroneCurrentStatusVO;
import com.ruoyi.device.api.domain.DroneFlightControlRequest;
import com.ruoyi.device.api.domain.DroneRealtimeInfoVO;
import com.ruoyi.device.api.domain.DroneTakeoffResponseVO;
import com.ruoyi.device.api.domain.MachineStateVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 无人机飞控服务降级处理
*
* @author ruoyi
* @date 2026-02-04
*/
@Component
public class RemoteAircraftFlyFallbackFactory implements FallbackFactory<RemoteAircraftFlyService>
{
private static final Logger log = LoggerFactory.getLogger(RemoteAircraftFlyFallbackFactory.class);
@Override
public RemoteAircraftFlyService create(Throwable throwable)
{
log.error("无人机飞控服务调用失败:{}", throwable.getMessage());
return new RemoteAircraftFlyService()
{
@Override
public R<Void> flightControl(DroneFlightControlRequest request, String source)
{
return R.fail("无人机飞控命令执行失败:" + throwable.getMessage());
}
@Override
public R<DroneRealtimeInfoVO> getRealtimeInfo(Long taskId, String source)
{
return R.fail("获取无人机实时信息失败:" + throwable.getMessage());
}
@Override
public R<DroneCurrentStatusVO> getCurrentStatus(Long dockId, String source)
{
return R.fail("查询无人机当前状态失败:" + throwable.getMessage());
}
@Override
public R<String> takeoff(String sn)
{
return R.fail("无人机起飞失败:" + throwable.getMessage());
}
@Override
public R<String> powerOn(String sn)
{
return R.fail("无人机开机失败:" + throwable.getMessage());
}
@Override
public R<String> powerOff(String sn)
{
return R.fail("无人机关机失败:" + throwable.getMessage());
}
@Override
public R<MachineStateVO> getMachineState(String sn)
{
return R.fail("查询设备状态失败:" + throwable.getMessage());
}
@Override
public R<String> coverOpen(String sn)
{
return R.fail("出舱失败:" + throwable.getMessage());
}
@Override
public R<String> coverClose(String sn)
{
return R.fail("回舱失败:" + throwable.getMessage());
}
@Override
public R<String> returnHome(String sn)
{
return R.fail("返航失败:" + throwable.getMessage());
}
};
}
}

View File

@ -0,0 +1,6 @@
com.ruoyi.device.api.factory.RemoteAircraftFallbackFactory
com.ruoyi.device.api.factory.RemoteAircraftFlyFallbackFactory
com.ruoyi.device.api.factory.RemoteDeviceFallbackFactory
com.ruoyi.device.api.factory.RemoteDockFallbackFactory
com.ruoyi.device.api.factory.RemoteGroupFallbackFactory
com.ruoyi.device.api.factory.RemoteStatisticsFallbackFactory

View File

@ -22,6 +22,20 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring Cloud OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -0,0 +1,81 @@
package com.ruoyi.task.api;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.task.api.domain.TaskPlanDTO;
import com.ruoyi.task.api.domain.TaskPlanQueryDTO;
import com.ruoyi.task.api.factory.RemoteTaskPlanFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 任务计划服务
*
* @author ruoyi
* @date 2026-03-06
*/
@FeignClient(contextId = "remoteTaskPlanService", value = ServiceNameConstants.TASK_SERVICE, fallbackFactory = RemoteTaskPlanFallbackFactory.class)
public interface RemoteTaskPlanService {
/**
* 创建定时任务计划
*
* @param taskPlanDTO 任务计划DTO
* @param source 请求来源
* @return 结果
*/
@PostMapping("/task/plan/timed")
R<Long> createTimedTaskPlan(@RequestBody TaskPlanDTO taskPlanDTO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 创建周期任务计划
*
* @param taskPlanDTO 任务计划DTO
* @param source 请求来源
* @return 结果
*/
@PostMapping("/task/plan/cycle")
R<Long> createCycleTaskPlan(@RequestBody TaskPlanDTO taskPlanDTO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 根据ID获取任务计划
*
* @param planId 计划ID
* @param source 请求来源
* @return 结果
*/
@GetMapping("/task/plan/{planId}")
R<TaskPlanDTO> getTaskPlanById(@PathVariable("planId") Long planId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 查询任务计划列表
*
* @param queryDTO 查询条件
* @param source 请求来源
* @return 结果
*/
@PostMapping("/task/plan/list")
R<List<TaskPlanDTO>> getTaskPlanList(@RequestBody TaskPlanQueryDTO queryDTO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 更新任务计划
*
* @param taskPlanDTO 任务计划DTO
* @param source 请求来源
* @return 结果
*/
@PutMapping("/task/plan")
R<Boolean> updateTaskPlan(@RequestBody TaskPlanDTO taskPlanDTO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 删除任务计划
*
* @param planId 计划ID
* @param source 请求来源
* @return 结果
*/
@DeleteMapping("/task/plan/{planId}")
R<Boolean> deleteTaskPlan(@PathVariable("planId") Long planId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -3,12 +3,14 @@ package com.ruoyi.task.api;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.constant.ServiceNameConstants;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.task.api.domain.TaskVO;
import com.ruoyi.task.api.domain.TaskTempVO;
import com.ruoyi.task.api.enums.StatusEnum;
import com.ruoyi.task.api.factory.RemoteTaskFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 任务服务
@ -20,12 +22,105 @@ import org.springframework.web.bind.annotation.RequestHeader;
public interface RemoteTaskService
{
/**
* 根据ID查询任务信息
* 根据ID查询任务临时表信息
*
* @param id 任务ID
* @param id 任务临时表ID
* @param source 请求来源
* @return 结果
*/
@GetMapping("/task/temp/{id}")
R<TaskTempVO> getTaskById(@PathVariable("id") String id, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
R<TaskTempVO> getTaskTempById(@PathVariable("id") String id, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 创建无关联计划的任务
*
* @param taskVO 任务DTO
* @param source 请求来源
* @return 结果
*/
@PostMapping("/task")
R<Long> createTaskWithoutPlan(@RequestBody TaskVO taskVO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 根据ID获取任务
*
* @param taskId 任务ID
* @param source 请求来源
* @return 结果
*/
@GetMapping("/task/{taskId}")
R<TaskVO> getTaskById(@PathVariable("taskId") Long taskId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
// /**
// * 查询任务列表
// *
// * @param queryDTO 查询条件
// * @param source 请求来源
// * @return 结果
// */
// @PostMapping("/task/list")
// R<List<TaskDTO>> getTaskList(@RequestBody TaskQueryVO queryDTO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 更新任务
*
* @param taskVO 任务DTO
* @param source 请求来源
* @return 结果
*/
@PutMapping("/task")
R<Boolean> updateTask(@RequestBody TaskVO taskVO, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 删除任务
*
* @param taskId 任务ID
* @param source 请求来源
* @return 结果
*/
@DeleteMapping("/task/{taskId}")
R<Boolean> deleteTask(@PathVariable("taskId") Long taskId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 根据无人机ID查询任务列表
*
* @param uavId 无人机ID
* @param source 请求来源
* @return 结果
*/
@GetMapping("/task/uav/{uavId}")
R<List<TaskVO>> getTaskByUavId(@PathVariable("uavId") String uavId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 根据无人机ID获取最新的一条任务
*
* @param uavId 无人机ID
* @param source 请求来源
* @return 结果
*/
@GetMapping("/task/uav/current/{uavId}")
R<TaskVO> getCurrentTaskByUavId(@PathVariable("uavId") String uavId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 修改执行状态
*
* @param taskId 任务ID
* @param status 任务状态
* @param source 请求来源
* @return 结果
*/
@PutMapping("/task/status/{taskId}")
R<Boolean> updateTaskStatus(@PathVariable("taskId") Long taskId, @RequestParam("status") StatusEnum status, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
/**
* 设置 recovery true
*
* @param taskId 任务ID
* @param source 请求来源
* @return 结果
*/
@PutMapping("/task/recovery/{taskId}")
R<Boolean> updateTaskRecovery(@PathVariable("taskId") Long taskId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}

View File

@ -0,0 +1,92 @@
package com.ruoyi.task.api.domain;
import com.ruoyi.task.api.enums.*;
import lombok.Data;
import java.util.Date;
/**
* 任务计划DTO
*
* @author ruoyi
* @date 2026-03-06
*/
@Data
public class TaskPlanDTO {
/**
* 任务计划ID
*/
private Long id;
/**
* 计划名称
*/
private String planName;
/**
* 计划类型
*/
private PlanTypeEnum planType;
/**
* 执行类型
*/
private ExecuteTypeEnum executeType;
/**
* 周期类型
*/
private CycleTypeEnum cycleType;
/**
* 周期值
*/
private String cycleValue;
/**
* 开始时间
*/
private Date startDate;
/**
* 结束时间
*/
private Date endDate;
/**
* 执行时间
*/
private Date executeTime;
/**
* 航线ID
*/
private Long routeId;
/**
* 无人机ID
*/
private String uavId;
/**
* 状态
*/
private StatusEnum status;
/**
* 描述
*/
private String description;
/**
* 持续时间分钟
*/
private Integer duration;
/**
* 航线文件URL
*/
private String routeUrl;
}

View File

@ -0,0 +1,53 @@
package com.ruoyi.task.api.domain;
import com.ruoyi.task.api.enums.CycleTypeEnum;
import com.ruoyi.task.api.enums.PlanTypeEnum;
import lombok.Data;
import java.util.Date;
/**
* 任务计划查询DTO
*
* @author ruoyi
* @date 2026-03-06
*/
@Data
public class TaskPlanQueryDTO {
/**
* 任务计划ID
*/
private Long id;
/**
* 计划类型
*/
private PlanTypeEnum planType;
/**
* 周期类型
*/
private CycleTypeEnum cycleType;
/**
* 航线ID
*/
private Long routeId;
/**
* 无人机ID
*/
private String uavId;
/**
* 开始时间起始
*/
private Date startDateStart;
/**
* 开始时间结束
*/
private Date startDateEnd;
}

View File

@ -0,0 +1,53 @@
package com.ruoyi.task.api.domain;
import com.ruoyi.task.api.enums.CycleTypeEnum;
import com.ruoyi.task.api.enums.PlanTypeEnum;
import lombok.Data;
import java.util.Date;
/**
* 任务查询DTO
*
* @author ruoyi
* @date 2026-03-06
*/
@Data
public class TaskQueryVO {
/**
* 任务ID
*/
private Long id;
/**
* 计划类型
*/
private PlanTypeEnum planType;
/**
* 周期类型
*/
private CycleTypeEnum cycleType;
/**
* 航线ID
*/
private Long routeId;
/**
* 无人机ID
*/
private Long uavId;
/**
* 开始时间起始
*/
private Date startDateStart;
/**
* 开始时间结束
*/
private Date startDateEnd;
}

View File

@ -0,0 +1,35 @@
package com.ruoyi.task.api.domain;
import java.util.List;
import java.util.Map;
/**
* 按年月统计任务DTO
*
* @author ruoyi
* @date 2026-03-09
*/
public class TaskStatByMonthVO {
/** 总数 */
private Integer total;
/** 每日任务列表 key:日期(1-31) value:任务列表 */
private Map<Integer, List<TaskStatItemVO>> days;
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Map<Integer, List<TaskStatItemVO>> getDays() {
return days;
}
public void setDays(Map<Integer, List<TaskStatItemVO>> days) {
this.days = days;
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.task.api.domain;
import java.util.Map;
/**
* 按年统计任务DTO
*
* @author ruoyi
* @date 2026-03-09
*/
public class TaskStatByYearDTO {
/** 总数 */
private Integer total;
/** 每月统计 key:月份(1-12) value:任务数量 */
private Map<Integer, Integer> months;
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public Map<Integer, Integer> getMonths() {
return months;
}
public void setMonths(Map<Integer, Integer> months) {
this.months = months;
}
}

View File

@ -0,0 +1,187 @@
package com.ruoyi.task.api.domain;
import com.ruoyi.task.api.enums.StatusEnum;
import com.ruoyi.task.api.enums.TaskCategoryEnum;
import com.ruoyi.task.api.enums.TaskTypeEnum;
import java.util.Date;
/**
* 任务统计项DTO
*
* @author ruoyi
* @date 2026-03-09
*/
public class TaskStatItemVO {
private Long taskId;
private Long planId;
private String taskName;
private String planName;
private String routeName;
private String airVendor;
private String airType;
private Date planStartDate;
private Date planEndDate;
private Date startTime;
private Date endTime;
private Date actualStartTime;
private Date actualEndTime;
private StatusEnum status;
private TaskTypeEnum taskType;
private TaskCategoryEnum taskCategory;
private String airlineType;
private Date taskCreateTime;
private String taskCreateBy;
public Long getTaskId() {
return taskId;
}
public void setTaskId(Long taskId) {
this.taskId = taskId;
}
public Long getPlanId() {
return planId;
}
public void setPlanId(Long planId) {
this.planId = planId;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getPlanName() {
return planName;
}
public void setPlanName(String planName) {
this.planName = planName;
}
public String getRouteName() {
return routeName;
}
public void setRouteName(String routeName) {
this.routeName = routeName;
}
public String getAirVendor() {
return airVendor;
}
public void setAirVendor(String airVendor) {
this.airVendor = airVendor;
}
public String getAirType() {
return airType;
}
public void setAirType(String airType) {
this.airType = airType;
}
public Date getPlanStartDate() {
return planStartDate;
}
public void setPlanStartDate(Date planStartDate) {
this.planStartDate = planStartDate;
}
public Date getPlanEndDate() {
return planEndDate;
}
public void setPlanEndDate(Date planEndDate) {
this.planEndDate = planEndDate;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getActualStartTime() {
return actualStartTime;
}
public void setActualStartTime(Date actualStartTime) {
this.actualStartTime = actualStartTime;
}
public Date getActualEndTime() {
return actualEndTime;
}
public void setActualEndTime(Date actualEndTime) {
this.actualEndTime = actualEndTime;
}
public StatusEnum getStatus() {
return status;
}
public void setStatus(StatusEnum status) {
this.status = status;
}
public TaskTypeEnum getTaskType() {
return taskType;
}
public void setTaskType(TaskTypeEnum taskType) {
this.taskType = taskType;
}
public TaskCategoryEnum getTaskCategory() {
return taskCategory;
}
public void setTaskCategory(TaskCategoryEnum taskCategory) {
this.taskCategory = taskCategory;
}
public String getAirlineType() {
return airlineType;
}
public void setAirlineType(String airlineType) {
this.airlineType = airlineType;
}
public Date getTaskCreateTime() {
return taskCreateTime;
}
public void setTaskCreateTime(Date taskCreateTime) {
this.taskCreateTime = taskCreateTime;
}
public String getTaskCreateBy() {
return taskCreateBy;
}
public void setTaskCreateBy(String taskCreateBy) {
this.taskCreateBy = taskCreateBy;
}
}

View File

@ -0,0 +1,116 @@
package com.ruoyi.task.api.domain;
import com.ruoyi.task.api.enums.StatusEnum;
import com.ruoyi.task.api.enums.TaskCategoryEnum;
import com.ruoyi.task.api.enums.TaskTypeEnum;
import java.util.Date;
import java.util.List;
/**
* 任务统计查询DTO
*
* @author ruoyi
* @date 2026-03-09
*/
public class TaskStatQueryVO {
/** 年份 */
private Integer year;
/** 月份 */
private Integer month;
/** 开始日期 */
private Date startTime;
/** 结束日期 */
private Date endTime;
/** 任务类别 */
private TaskCategoryEnum taskCategory;
/** 任务类型 */
private TaskTypeEnum taskType;
/** 状态列表 */
private List<StatusEnum> statusList;
/** 航线ID列表 */
private List<Long> routeIdList;
/** 无人机ID列表 */
private List<String> uavIdList;
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getMonth() {
return month;
}
public void setMonth(Integer month) {
this.month = month;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public TaskCategoryEnum getTaskCategory() {
return taskCategory;
}
public void setTaskCategory(TaskCategoryEnum taskCategory) {
this.taskCategory = taskCategory;
}
public TaskTypeEnum getTaskType() {
return taskType;
}
public void setTaskType(TaskTypeEnum taskType) {
this.taskType = taskType;
}
public List<StatusEnum> getStatusList() {
return statusList;
}
public void setStatusList(List<StatusEnum> statusList) {
this.statusList = statusList;
}
public List<Long> getRouteIdList() {
return routeIdList;
}
public void setRouteIdList(List<Long> routeIdList) {
this.routeIdList = routeIdList;
}
public List<String> getUavIdList() {
return uavIdList;
}
public void setUavIdList(List<String> uavIdList) {
this.uavIdList = uavIdList;
}
}

View File

@ -0,0 +1,226 @@
package com.ruoyi.task.api.domain;
import com.ruoyi.task.api.enums.ExecuteTypeEnum;
import com.ruoyi.task.api.enums.StatusEnum;
import com.ruoyi.task.api.enums.TaskCategoryEnum;
import com.ruoyi.task.api.enums.TaskTypeEnum;
import java.util.Date;
/**
* 任务数据传输对象
*
* @author ruoyi
* @date 2026-03-05
*/
public class TaskVO {
/** 任务ID */
private Long id;
/** 计划ID可为空如一键起飞 */
private Long planId;
/** 任务名称 */
private String taskName;
/** 任务类别(如人工执飞) */
private TaskCategoryEnum taskCategory;
/** 任务类型(如一键起飞) */
private TaskTypeEnum taskType;
/** 执行类型(单次执行、连续执行) */
private ExecuteTypeEnum executeType;
/** 航线ID */
private Long routeId;
/** 无人机ID */
private String uavId;
/** 状态 */
private StatusEnum status;
/** 错误是否恢复 */
private Boolean recovery;
/** 开始时间 */
private Date startTime;
/** 结束时间 */
private Date endTime;
/** 实际开始时间 */
private Date actualStartTime;
/** 实际结束时间 */
private Date actualEndTime;
/** 描述 */
private String description;
/** 备注 */
private String remark;
/** 航线文件URL */
private String routeUrl;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getPlanId() {
return planId;
}
public void setPlanId(Long planId) {
this.planId = planId;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public TaskCategoryEnum getTaskCategory() {
return taskCategory;
}
public void setTaskCategory(TaskCategoryEnum taskCategory) {
this.taskCategory = taskCategory;
}
public TaskTypeEnum getTaskType() {
return taskType;
}
public void setTaskType(TaskTypeEnum taskType) {
this.taskType = taskType;
}
public ExecuteTypeEnum getExecuteType() {
return executeType;
}
public void setExecuteType(ExecuteTypeEnum executeType) {
this.executeType = executeType;
}
public Long getRouteId() {
return routeId;
}
public void setRouteId(Long routeId) {
this.routeId = routeId;
}
public String getUavId() {
return uavId;
}
public void setUavId(String uavId) {
this.uavId = uavId;
}
public StatusEnum getStatus() {
return status;
}
public void setStatus(StatusEnum status) {
this.status = status;
}
public Boolean getRecovery() {
return recovery;
}
public void setRecovery(Boolean recovery) {
this.recovery = recovery;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getActualStartTime() {
return actualStartTime;
}
public void setActualStartTime(Date actualStartTime) {
this.actualStartTime = actualStartTime;
}
public Date getActualEndTime() {
return actualEndTime;
}
public void setActualEndTime(Date actualEndTime) {
this.actualEndTime = actualEndTime;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getRouteUrl() {
return routeUrl;
}
public void setRouteUrl(String routeUrl) {
this.routeUrl = routeUrl;
}
@Override
public String toString() {
return "TaskDTO{" +
"id=" + id +
", planId=" + planId +
", taskName='" + taskName + '\'' +
", taskCategory=" + taskCategory +
", taskType=" + taskType +
", executeType=" + executeType +
", routeId=" + routeId +
", uavId=" + uavId +
", status=" + status +
", startTime=" + startTime +
", endTime=" + endTime +
", actualStartTime=" + actualStartTime +
", actualEndTime=" + actualEndTime +
", description='" + description + '\'' +
", routeUrl='" + routeUrl + '\'' +
'}';
}
}

View File

@ -0,0 +1,32 @@
package com.ruoyi.task.api.enums;
public enum CycleTypeEnum {
DAILY("daily", "日周期"),
WEEKLY("weekly", "周周期"),
MONTHLY("monthly", "月周期");
private final String code;
private final String name;
CycleTypeEnum(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public static CycleTypeEnum getByCode(String code) {
for (CycleTypeEnum type : values()) {
if (type.code.equals(code)) {
return type;
}
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More