基于若依框架的无人机智慧巡检后端实战:状态机与双数据源设计

发布时间:2026/10/30 16:38:51
基于若依框架的无人机智慧巡检后端实战:状态机与双数据源设计 简介这份资源是基于若依框架构建的无人机智慧巡检后端系统源码面向需要实现无人机实时视频流检测报警、数据报表查看和大屏可视化展示的Java后端开发人员。源码共405个文件主体为333个Java源文件搭配XML与YAML配置文件、VM模板文件以及PNG/JPG等界面图片压缩包约11.3MB目录结构清晰便于模块化检索与后续维护。目前已有362人学习下载。系统利用若依框架的轻量级快速开发特性实现了前后端分离架构包含数据库脚本、Maven配置和readme说明能够帮助读者从环境搭建、配置管理到核心功能实现形成完整认知。源码尤其突出实时视频流处理与报警逻辑以及大屏数据的可视化呈现适合作为无人机智慧巡检领域后端技术的学习参考和二次开发基础也可直接用于相关项目的快速原型搭建与功能扩展。1. 无人机智慧巡检后端为什么我选若依而不是从零搭项目前几天帮一家做园区安防的公司做无人机智慧巡检系统客户第一句话就是“任务在后台下发飞手在平板收到飞机飞完自动上传 4K 红外图缺陷能在网页上圈出来后台最好能直接看到无人机起降平台的状态。”面对这种需求很多后端第一反应是用若依框架快速搭权限和菜单但真正动手才发现若依能给你用户、角色、菜单、定时任务、代码生成器却不会告诉你巡检任务的状态机怎么设计更不会告诉你航线上传、双数据源、平台对接这些坑怎么填。这套基于若依框架的无人机智慧巡检后端设计源码做的就是这件事。它面向的读者不是架构师而是手里有实际巡检业务、想用 Java 生态快速交付的 Java 后端工程师以及做智能硬件接入的技术负责人。2. 若依框架下的项目落地从环境准备到跑通第一个巡检业务模块2.1 为什么选若依权限模型、代码生成器和定时任务正好卡在巡检场景痛点我先说结论如果只是做一个内部信息管理系统那用不用若依无所谓但无人机智慧巡检后端天然带着角色多、任务重、状态多的特点若依的四个能力几乎是一对一匹配的。第一是权限模型巡检系统中管理员要看全局飞手只能看自己的任务审核员只能看已完成的报告这种数据权限若依已经帮你实现到注解级别。第二是代码生成器设备表、任务表、报告表都是标准 CRUD用生成器先搭出骨架比纯手写快一个下午。第三是定时任务航线预巡检、无人机电量低提醒、巡检报告日度拉取都需要任务调度。第四是操作日志和在线用户管理客户后期查问题的时候特别好用。从零搭一套这些功能按正常开发效率至少要加两个星期而用若依你的主要精力可以放在业务逻辑上。而且若依本身是前后端分离项目后端是 Spring Boot前端是 Vue正好与无人机巡检中“后台配置、移动端接收”的多端场景兼容。有人说若依太重但巡检后端涉及的菜单、角色、部门、字典本身就是业务的一部分框架的骨架能省去重复的分页查询和权限判断。若依的代码生成器不是简单生成 CRUD它生成的 domain、mapper、service、controller 以及前端 vue 文件能让你把注意力集中在巡检业务上。所以我的建议是接这一类无人机项目只要客户的操作端是网页就值得优先评估若依。需求点若依框架自带从零搭建的成本用户角色权限sys_user/sys_role 完整 RBAC1-2 周菜单与按钮权限动态菜单 权限注解1 周定时任务Quartz 封装3-5 天操作日志切面注入2-3 天代码生成器表结构生成前后端代码难以估量2.2 从下载到启动源码包目录结构与后端启动步骤拿到这套源码后第一件事不是急着改代码而是把包结构和跑通方法的顺序捋清楚。虽然不同版本目录有差异但一般遵循若依标准结构ruoyi-admin是启动模块ruoyi-framework放框架配置登录校验、异常处理ruoyi-system放系统管理相关的业务ruoyi-common放工具类然后还有专门给巡检业务建的ruoyi-biz模块。如果你看到的源码包里有整合 Quarts 的场景那也没必要慌定时任务调度通常就放在这一层里。启动步骤我一般按下面三步走# 第一步创建数据库导入若依初始化脚本和巡检业务表脚本 # 注意源码包里的 sql 目录下一般有 ruoyi 基础库和 biz 业务库两个脚本顺序不能反 mysql -uroot -p -e CREATE DATABASE ruoyi DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -uroot -p ruoyi sql/ruoyi_init.sql mysql -uroot -p ruoyi sql/biz_inspection.sql # 第二步修改数据库连接、Redis 连接等配置 # 路径通常在 ruoyi-admin/src/main/resources/application-druid.yml vim ruoyi-admin/src/main/resources/application-druid.yml # 第三步启动后端默认端口 8080 cd ruoyi-admin mvn spring-boot:run第一条命令的DEFAULT CHARACTER SET utf8mb4一定要带上。无人机巡检记录里可能有 emoji 备注不带的话最后写入图片标注或设备别名的时候会出现Incorrect string value报错。第三条命令的mvn spring-boot:run是开发环境最直观的做法如果你已经装好了 maven 和 JDK 17 的话这一步基本不需要改代码就能看到 Tomcat started 日志。参数方面-uroot -p代表用户名和密码实际使用时把密码补上Redis 配置在另一个 yml 里若依的前后端分离必须依赖 Redis 做会话缓存这一点能坑掉很多人。提示数据库脚本导入顺序错了后边巡检菜单会 404。先导入若依基础脚本再导入巡检业务脚本因为业务菜单依赖sys_menu表的父级记录。2.3 用代码生成器生成无人机设备模块再动手补业务字段如果只是把后台跑起来那还没有体现这份源码的价值。真正让你进入角色的是用若依的代码生成器生成无人机设备表模块。在系统管理→代码生成里导入一张biz_drone表然后生成代码。生成器会让你配置生成路径、模块包名、表前缀等。这里给一个典型的表脚本-- 无人机设备表若依代码生成建议直接用单表避免一次性生成主表加子表 DROP TABLE IF EXISTS biz_drone; CREATE TABLE biz_drone ( drone_id bigint NOT NULL AUTO_INCREMENT COMMENT 无人机ID, drone_sn varchar(64) NOT NULL COMMENT 无人机SN, drone_name varchar(128) DEFAULT NULL COMMENT 设备名称, model varchar(64) DEFAULT NULL COMMENT 型号, dock_id varchar(32) DEFAULT NULL COMMENT 起降平台编号, battery_level int DEFAULT NULL COMMENT 电量百分比, last_flight_time datetime DEFAULT NULL COMMENT 最后起飞时间, status char(1) DEFAULT 0 COMMENT 状态0待机 1飞行中 2返航 3离线, remark varchar(500) DEFAULT NULL COMMENT 备注, PRIMARY KEY (drone_id) ) ENGINEInnoDB COMMENT无人机设备表;导入生成器后把包名改成类似com.ruoyi.biz.drone生成后代码会落在临时目录然后复制到ruoyi-biz模块。要注意的是生成器默认会把drone_id当成普通字段不会自动设为雪花 ID 或自增你需要确认 Controller 里Service注入是否正确。另外last_flight_time这种时间字段在生成的前端 form 表单里默认是 text 类型你需要手动改为 datetime 控件否则保存时格式会报 500。生成完后我一般会在 Service 接口里额外加一个updateDroneStatus(String droneSn, String status)方法这是因为无人机飞控上传的状态数据是高频的实时心跳不适合走到 Controller 再进 Service而是应该通过消息队列直接消费。这一改造就是这套源码里最值得看的部分它会直接影响下一章要讲的巡检任务调度。3. 巡检任务调度与航线下发TDengine 双数据源和状态机实战3.1 巡检任务的状态机设计从待执行到已完成状态改变必须留痕无人机巡检任务和普通审批任务最大的区别在于它存在“飞控回调”这个外部触达点。任务不是人工一步步点的而是后端根据计划时间自动下发再接收无人机起降平台的状态回传。所以任务表的状态必须是一个明确的状态机否则后续对账、重试、数据统计都会乱。在这套源码里巡检任务的状态被定义为四个值核心流转关系如下状态值状态名称触发条件下游动作0待执行管理员创建任务或系统预排定时扫描到点任务并下发1进行中起降平台确认起飞实时记录轨迹2已完成飞控回传完成信号生成巡检报告入口3失败超时或设备异常告警并允许重试有些人喜欢把状态直接写成 String 然后到处判断status.equals(2)第一版确实快但后面加一个“已取消”状态时你会想骂人。我在这套源码里看到的做法是先把状态定义在字典里然后用一个常量类封装。这样定时任务调度、人工改单、飞控回调都走同一个changeStatus方法每次修改都会写入状态变更日志表客户查起历史来一目了然。任务状态变更日志表至少要有task_id、old_status、new_status、change_by、change_time五个字段这不算过度设计巡检业务出问题时靠它定位责任。3.2 定时任务扫描到点任务cron 表达式与运行实例的取舍巡检任务下发是典型的定时任务场景。管理员可能一次性批量导入 50 条任务这些任务计划开始时间分散在未来 24 小时内如果都靠人工触发飞手得守着后台刷新。源码里的做法是写一个后台扫描器每 5 分钟扫一次未执行任务到点就下发。/** * 自动巡检任务扫描 * 每 5 分钟扫描一次找出计划开始时间在 now 之前且状态为 0 的任务 */ Scheduled(cron 0 0/5 * * * ?) public void autoDispatch() { ListInspectionTask tasks taskMapper.selectPendingTask(LocalDateTime.now()); if (CollectionUtils.isEmpty(tasks)) { return; } for (InspectionTask task : tasks) { try { task.setStatus(TaskStatus.IN_PROGRESS); taskMapper.updateStatus(task); // 组装航线并推送给无人驾驶起降平台Dock dockClient.dispatch(task.getDockId(), task.getTaskId()); log.info([autoDispatch] 任务 {} 下发给起降平台 {}, task.getTaskNo(), task.getDockId()); } catch (Exception e) { log.error([autoDispatch] 任务 {} 下发异常, task.getTaskNo(), e); task.setStatus(TaskStatus.FAILED); taskMapper.updateStatus(task); } } }selectPendingTask对应的 Mapper SQL 条件是status 0 and plan_start now()。这里注意定时任务方法内的事务边界不要覆盖整个循环否则一个任务下发失败会把整个批次回滚。我的习惯是每个任务单独提交一次失败只标记当次任务。cron表达式0 0/5 * * * ?表示从 0 分开始每 5 分钟触发一次如果你希望精确到 1 分钟改成0 * * * * ?。另外如果你部署了多个应用节点同一个任务会被多个节点同时扫描到这就是重复下发。常见做法是给task_id加一个唯一索引配合UPDATE ... WHERE status0的乐观更新或者直接用 Redis 分布式锁。在若依框架里我一般选择后者因为 Redis 本来就是必需的开箱即用。加锁的细节在第 4 章的避坑清单里会专门讲。3.3 双数据源接入业务表放 MySQL飞行轨迹放 TDengine无人机起飞后飞控会以每秒至少一次的频率上送高度、经纬度、速度、电池电压这些数据如果全部塞进 MySQL巡检任务一多单表能轻松到几百万行查询轨迹回放时明显变慢。所以源码里把时序数据放到 TDengine业务数据留在 MySQL。要在一个若依项目里同时使用两个数据源常见做法是引入dynamic-datasource动态数据源插件然后以 MySQL 为主库TDengine 为从库。spring: datasource: dynamic: primary: mysql strict: true datasource: mysql: url: jdbc:mysql://localhost:3306/ruoyi?useUnicodetruecharacterEncodingutf8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root tdsql: url: jdbc:taos://localhost:6030/patrol_db driver-class-name: com.taosdata.jdbc.TSDBDriver username: root password: taosdata tomcat: max-active: 10然后是轨迹写入 Mapper 的写法DS(tdsql) Mapper public interface TrackPointMapper { Insert(insert into track_point (ts, task_id, lng, lat, alt, speed) values (#{ts}, #{taskId}, #{lng}, #{lat}, #{alt}, #{speed})) int saveTrackPoint(TrackPoint point); }DS注解可以加在 Mapper 类或方法上加在类上时整个 Mapper 的所有操作都走 TDengine。primary: mysql表示默认数据源还是 MySQL所以只有标注了DS(tdsql)的代码才会切换过去不会影响若依自身的权限、日志这些表。这里要注意TDengine 的 JDBC URL 端口是 6030不是 MySQL 的 3306。如果你的 TDengine 是集群需要在 taos.cfg 里配置多个 EP 列表而不是在 URL 里写逗号。把飞行轨迹写入 TDengine 和更新任务状态放在同一个事务方法里时你可能会发现失败后无法回滚。这是动态数据源切换的天然缺陷我在下一章会专门展开它是我在巡检项目中踩得最深的一个坑。4. 若依 无人机后端避坑清单权限、双数据源与定时任务4.1 菜单生成后 404代码生成器只给了代码没给路由现象用若依代码生成器生成设备管理模块重启后端登录系统后在菜单列表里找不到或者手动配置菜单后点击一直白屏 / 404。原因若依的前端路由是动态生成的前端会请求后端返回的菜单列表然后按组件路径映射到页面。如果菜单配置中组件路径写错或者没有新增菜单权限前端就不会加载对应路由。很多新手只把代码复制进ruoyi-biz忘了在sys_menu表里插入一条记录。解决手动插入菜单记录关键字段如下INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon) VALUES (无人机设备管理, 0, 1, drone, biz/drone/index, 1, 0, C, 0, 0, biz:drone:list, device);component必须是前端源码里实际存在的views/biz/drone/index.vue否则页面会 404。perms必须与后端PreAuthorize(ss.hasPermi(biz:drone:list))保持一致。如果你拿到的源码包自己没有用这个包名把biz/drone换成你实际的控制器路由即可。4.2 TDengine 写入数据无法回滚事务边界要拆开现象一个方法里先向 MySQL 更新任务状态再往 TDengine 插入轨迹MySQL 更新失败后抛出异常但 TDengine 已经写入的轨迹还留在表里导致任务状态与轨迹数据不对应。原因动态数据源DS切换后事务管理器默认还是 MySQL 的TDengine 是非事务型数据库不可能参与两阶段提交。换句话说两个数据源之间没有跨库事务失败后自然不能一起回滚。解决把两件事拆分。一种做法是先记录轨迹数据到本地消息表MySQL再异步消费并写入 TDengine另一种做法是把 TDengine 写入放在事务提交之后失败时通过重试或补偿清理。我在这套源码里使用的是第二种具体来说就是在方法上加Transactional(propagation Propagation.NOT_SUPPORTED)的独立轨迹上报入口让它脱离主事务。注意这样做的代价是轨迹上报可能重复需要在写入语句里用task_id加ts做唯一约束。4.3 定时任务执行了两次多实例部署没有做互斥现象两台应用服务器同时启动第二天发现同一个巡检任务被下发两次无人机收到两条重复航线指令。原因SpringScheduled是进程内调度多个实例各跑各的Quartz 用 JDBC 集群模式时如果触发器配置不当也可能重复。在分布式部署下定时任务必须做互斥。解决在任务扫描前加 Redis 锁。常见代码是String lockKey lock:autoDispatch: task.getTaskId(); // 尝试加锁超时 3 秒成功后 30 秒自动释放 if (!redisTemplate.opsForValue().setIfAbsent(lockKey, 1, Duration.ofSeconds(30))) { log.warn([autoDispatch] 任务 {} 已经有节点在处理跳过, task.getTaskNo()); continue; } taskMapper.updateStatus(task);锁的过期时间 30 秒要大于任务处理的最大时间避免任务没处理完锁就过期另一个节点又进来处理。如果任务里还包含大图上传或航线同步建议把过期时间调成 60 秒并且把释放锁的动作放在finally里。4.4 跨域问题若依后端默认只允许本机前端现象前端挂在服务器上用 IP 访问页面时登录无法保存 Session接口全部跨域报错。原因若依的跨域配置默认只对localhost和127.0.0.1生效没有把实际前端使用的 IP 或域名加进去。检查浏览器 Network 面板通常能看到CORS error。解决在application.yml里找到若依的 cors 配置把allowedOriginPatterns改成实际部署地址。注意不能用*否则带凭证的请求会被浏览器拒绝。正确写法是把前端地址一一列出比如cors: allowed-origin-patterns: - http://192.168.1.100:80 - https://patrol.example.com如果前端和后端通过 Nginx 同域转发那只需要在后端配置上允许same-origin即可。每次部署新环境我会先把 Origin 配置列成一个显式的白名单避免排查时浪费时间。5. 一个进阶技巧用若依数据权限控制巡检任务归属再验证接口权限5.1 数据权限注解让飞手只看自己的任务巡检系统上线后最麻烦的需求不是功能而是数据边界飞手 A 不应该看到飞手 B 的任务记录部门领导可以看整个部门的记录。若依专门提供了DataScope注解但很多人在业务表里没有正确设置user_id和dept_id导致注解没有作用。正确的做法是在查询方法上同时加权限注解和数据权限注解GetMapping(/list) PreAuthorize(ss.hasPermi(biz:task:list)) DataScope(deptAlias ts) public TableDataInfo list(TaskQuery query) { startPage(); ListInspectionTask list taskService.selectTaskList(query); return getDataTable(list); }这里的deptAlias必须和 SQL 里的表别名一致。并且 Mapper XML 中需要在查询 SQL 末尾追加${params.dataScope}这样若依的拦截器才会把当前用户的部门和数据范围条件拼接进去。例如select idselectTaskList parameterTypeTaskQuery resultTypeInspectionTask select ts.task_id, ts.task_no, ts.dock_id, ts.drone_sn, ts.status from biz_inspection_task ts where if testtaskNo ! nullAND ts.task_no like concat(%, #{taskNo}, %)/if /where ${params.dataScope} /select${params.dataScope}是若依数据权限拦截器生成的 SQL 片段比如AND ts.dept_id in (103)。要保证这条条件能正常拼接biz_inspection_task表里必须有dept_id字段并且在插入任务时把创建人的deptId一起写入。这是很多人忽略的地方只给表加了create_by没加dept_id结果部门领导看不到下属任务。5.2 用 curl 验证权限控制是否生效验证方式不能只靠前端我一般用后端接口来测试。先拿管理员登录拿 token再拿一个飞手账号登录两次访问同一个任务列表接口对比返回数据的条数差异curl -X GET http://your-server:8080/biz/task/list?pageNum1pageSize10 \ -H Authorization: Bearer admin-token | head -c 500curl -X GET http://your-server:8080/biz/task/list?pageNum1pageSize10 \ -H Authorization: Bearer pilot-token | head -c 500如果飞手账号的响应里只有自己被创建的task_id说明数据权限生效如果它列出了所有部门任务说明dept_id没有正确写入。若依的 token 前缀是Bearer中间有一个空格不要把Bearer和 token 挤在一起。自从有一次把一串任务数据在客户现场演示时不小心全量拉出来了从那以后我每次在若依里加巡检相关接口都会强制用两个不同角色的账号走一遍 curl 对比确认数据边界没问题再交给测试。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询