ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于若依框架的无人机智慧巡检后端:表设计、状态机与避坑指南

基于若依框架的无人机智慧巡检后端:表设计、状态机与避坑指南 简介一套基于若依框架的无人机智慧巡检后端设计源码面向Java后端开发者与无人机巡检系统设计人员定位是提供一套可直接参考的完整后端解决方案。系统支持实时视频流检测报警、报表数据查看与大屏数据展示可满足电力巡检、农业监测等场景下的数据处理与可视化需求。压缩包共405个文件大小11.3MB以333个Java源文件为主辅以XML、YAML配置文件VM模板文件PNG/JPG等界面图片以及SQL脚本、properties属性文件等构成完整的后端工程结构。其中SQL脚本可用于快速搭建数据库环境配合pom.xml等构建配置便于理解Maven构建与多环境配置方式。目前已有362人学习浏览适合希望掌握若依框架快速开发、后端模块化设计及前后端分离架构的读者。通过阅读源码可学习实时数据接入、报警处理、报表与大屏接口设计等实践思路是一份具有较强参考价值的无人机智慧巡检后端工程范例。1. 基于若依框架的无人机智慧巡检后端先想清楚它到底解决什么问题一个做输电线路巡检的团队来找我时需求说得很简单把无人机拍回来的照片、飞过的航线、发现的缺陷、派出去的维修单统一在一个后台里管起来。项目排了三个月真正耗时间的不是登录权限不是菜单管理而是任务怎么下发、数据怎么回传、缺陷怎么转成工单。若依框架的价值恰好在这里——RuoYi 这类前后端分离的框架把你最不想重复的系统底座全部做好无人机智慧巡检后端源码要解决的是把巡检业务模型长在若依身上。这篇文章面向要落地这类项目后端开发的读者讲清楚业务边界、表设计、状态流转以及最容易翻车的地方。2. 拆开巡检业务域若依框架做底座的边界与四张核心表设计2.1 若依框架在巡检后端里的边界能接住的是底座接不住的是业务先给结论若依框架前后端分离版的核心能力是“系统管理”。Spring Boot Spring Security MyBatis 这套组合加上代码生成器、定时任务、操作日志、数据权限注解开箱即用。这些能力对于无人机智慧巡检后端来说属于地基而不是房子。地基可以省掉大量重复劳动但巡检业务本身得自己设计。我一般会把若依能接住的部分和接不住的部分列成一张对照表给团队成员做分工。能接住的是用户、角色、部门、岗位这套 RBAC 权限模型单表增删改查的代码生成器基于 Quartz 的定时任务管理登录日志与操作日志基于注解的部门数据权限。接不住的是无人机设备状态的实时同步航线与任务的状态机流转图片和视频数据回传后的落盘与检索AI 识别结果回写时的并发控制缺陷从发现到归档的工单闭环。这四块接不住的部分恰恰是无人机智慧巡检后端的关键。很多团队把若依代码生成器生成的源码直接当成业务系统上线结果发现巡检任务没有状态变化、缺陷数据没有流转逻辑最后只能临时改表结构。所以动手建表前先把“设备—任务—数据—缺陷”这条主线拆清楚。2.2 四张核心业务表设备、任务、任务点、缺陷工单先给一套我在多个巡检项目里沉淀下来的表结构。这套表不追求大而全目标是能支撑一条完整的巡检业务链设备档案、任务计划、航线点、缺陷结果。设备表存的是无人机和机场的静态档案与在线状态CREATE TABLE device ( device_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 设备ID, device_no varchar(64) NOT NULL COMMENT 设备编号, device_name varchar(128) NOT NULL COMMENT 设备名称, device_type varchar(32) DEFAULT UAV COMMENT 设备类型:UAV无人机,AIRPORT机场,CAMERA挂载相机, gps_lng decimal(10,6) DEFAULT NULL COMMENT 安装经度, gps_lat decimal(10,6) DEFAULT NULL COMMENT 安装纬度, device_status varchar(20) DEFAULT OFFLINE COMMENT 设备状态:ONLINE在线,OFFLINE离线,BUSY作业中, dept_id bigint(20) DEFAULT NULL COMMENT 归属部门, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, remark varchar(500) DEFAULT NULL COMMENT 备注, PRIMARY KEY (device_id), UNIQUE KEY uk_device_no (device_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT巡检设备表;注意device_status我用了字符串的字典值而不是数字这样前端下拉和若依的字典管理可以直接对应。dept_id是为后续若依的数据权限注解准备的跨部门协作时这条字段的价值会在避坑章节里体现。经纬度用decimal(10,6)精度足够到米级字符串存经纬度是巡检项目里常见的坏味道。任务表是整条业务链的中枢CREATE TABLE patrol_task ( task_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 任务ID, task_no varchar(32) NOT NULL COMMENT 任务编号, task_name varchar(128) NOT NULL COMMENT 任务名称, device_id bigint(20) DEFAULT NULL COMMENT 设备ID, route_id bigint(20) DEFAULT NULL COMMENT 航线ID, status varchar(20) NOT NULL DEFAULT DRAFT COMMENT 状态:DRAFT草稿,PENDING待执行,EXECUTING执行中,RETURNED已回传,ANALYZING分析中,FINISHED已完成,ARCHIVED已归档, plan_start_time datetime DEFAULT NULL COMMENT 计划开始时间, actual_start_time datetime DEFAULT NULL COMMENT 实际开始时间, plan_end_time datetime DEFAULT NULL COMMENT 计划结束时间, actual_end_time datetime DEFAULT NULL COMMENT 实际结束时间, create_by varchar(64) DEFAULT COMMENT 创建人, create_time datetime DEFAULT NULL COMMENT 创建时间, update_by varchar(64) DEFAULT COMMENT 更新人, update_time datetime DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (task_id), UNIQUE KEY uk_task_no (task_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT巡检任务表;task_no我建议生成规则为日期加序号比如20250601-001这样线下沟通时直接报编号就能定位任务比自增 ID 好用得多。状态字段status是后面状态机设计的落点不要在业务代码里随便写1、2这种数字后文会展开说。任务点表记录航线上的每个航点用于回放和按点位关联缺陷CREATE TABLE patrol_task_point ( point_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 航点ID, task_id bigint(20) NOT NULL COMMENT 任务ID, point_index int(11) DEFAULT NULL COMMENT 航点序号, gps_lng decimal(10,6) DEFAULT NULL COMMENT 经度, gps_lat decimal(10,6) DEFAULT NULL COMMENT 纬度, altitude decimal(8,2) DEFAULT NULL COMMENT 高度(米), action varchar(32) DEFAULT HOVER COMMENT 动作:HOVER悬停,SHOOT拍照,ZOOM变焦, create_time datetime DEFAULT NULL, PRIMARY KEY (point_id), KEY idx_task_id (task_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT巡检任务航点表;航点单独成表而不是在任务表里存一个 JSON 数组原因很简单任务回放和缺陷定位都要按点查数据JSON 字段在 MySQL 里查询和统计都很被动。这里建了idx_task_id索引按任务查询航点的场景是最频繁的。缺陷工单表承载 AI 识别和人工判读的结果CREATE TABLE defect_order ( defect_id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 缺陷ID, task_id bigint(20) DEFAULT NULL COMMENT 任务ID, device_id bigint(20) DEFAULT NULL COMMENT 设备ID, point_id bigint(20) DEFAULT NULL COMMENT 航点ID, defect_type varchar(32) DEFAULT NULL COMMENT 缺陷类型:BIRD_NEST鸟巢,INSULATOR_DAMAGE绝缘子破损,HEAT_SPOT发热点, defect_level varchar(10) DEFAULT LOW COMMENT 缺陷级别:HIGH高,MEDIUM中,LOW低, image_url varchar(255) DEFAULT NULL COMMENT 缺陷图片路径, ai_score decimal(5,2) DEFAULT NULL COMMENT AI置信度, description varchar(500) DEFAULT NULL COMMENT 问题描述, process_status varchar(20) DEFAULT PENDING COMMENT 处理状态:PENDING待处理,PROCESSING处理中,DONE已处理, create_time datetime DEFAULT NULL COMMENT 发现时间, PRIMARY KEY (defect_id), KEY idx_task_id (task_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT缺陷工单表;四张表的关系是设备参与多个任务任务包含多个航点航点上挂多个缺陷缺陷。实际项目里还可以加一张patrol_record表记录每次飞行产生的原始数据文件但上面这四张是业务闭环的最小集合。表结构定了下一步就要管住任务状态怎么流转。3. 任务从生成到归档用状态机管住无人机巡检全流程3.1 任务状态定义把字典值设计到位后续少改代码任务状态是整个巡检后端里最容易写乱的地方。很多项目一开始只在代码里写几个 if 判断上线后发现要加“撤回”“暂停”“失败重试”这些状态所有判断都要跟着改等于把状态逻辑埋进了黑匣子。我在巡检项目里一律按状态机设计先把状态和允许的流转画在纸上再动代码。推荐一组状态定义状态值含义下一步动作DRAFT草稿任务刚生成下发PENDING待执行已下发执行或撤回EXECUTING执行中无人机已起飞回传RETURNED已回传数据落地完成开始分析ANALYZING分析中AI 识别或人工判读完成或生成缺陷FINISHED已完成缺陷已闭环归档ARCHIVED已归档只读无这套状态在若依里可以做成字典在sys_dict_type下增加patrol_task_status字典项和上表保持一致。这样前端下拉框、后端校验、列表筛选全都复用同一份数据不会出现前端显示“执行中”、后端判断的是“EXECUTING”这种脱节。流程里的每个动作对应一条状态迁移不允许跳转。3.2 用若依的定时任务做待下发扫描Quartz 与 Scheduled 怎么选巡检任务往往需要定时生成比如每天凌晨根据天气窗口自动创建当天任务。若依框架自带定时任务管理模块底层是 Quartz可以在管理页面上配置 bean 名称、调用方法和 cron 表达式不需要改代码就能调整调度频率。这也是我建议优先用若依自带定时任务而不是自己写Scheduled注解的原因Scheduled写死在代码里上线后改频率要重新发版巡检项目中调度需求变化非常频繁这种灵活性很重要。在若依的定时任务页面里添加任务时调用目标字符串写成patrolTaskService.scanNeedDispatch调用方法写scanNeedDispatchcron 表达式按需配置。比如每天凌晨检查一次用0 0 2 * * ?如果飞行窗口受天气影响大可以缩短为每半小时一次0 0/30 * * * ?。配置完成后若依会把执行记录写到调度日志里失败时能看到堆栈这比裸写Scheduled多了一层可观测性。3.3 状态流转的代码落点乐观更新防止并发重复下发状态机不能只是设计文档落代码时最容易出问题的是并发。比如两个人同时点了“下发”或者定时扫描和手工操作同时触发任务可能被重复下发到无人机机场。我用的是“条件更新 受影响行数判断”的方式伪代码示例如下Service public class PatrolTaskServiceImpl implements PatrolTaskService { Autowired private PatrolTaskMapper patrolTaskMapper; Transactional(rollbackFor Exception.class) public void dispatch(Long taskId) { // 1. 查当前任务确认当前状态 PatrolTask task patrolTaskMapper.selectById(taskId); if (task null) { throw new ServiceException(任务不存在); } // 2. 只有草稿状态才能下发其他状态直接拒绝 if (!DRAFT.equals(task.getStatus())) { throw new ServiceException(当前状态不允许下发); } // 3. 用 update ... where status旧状态 做乐观校验 int rows patrolTaskMapper.updateStatus(taskId, DRAFT, PENDING); // 4. 受影响行数为0说明状态已被别人改过 if (rows 0) { throw new ServiceException(任务已被其他操作更新请刷新后重试); } } }updateStatus对应的 Mapper 语句是这个思路的关键UPDATE patrol_task SET status #{newStatus}, update_time now() WHERE task_id #{taskId} AND status #{oldStatus}这里没有用悲观锁SELECT FOR UPDATE原因是在任务下发这种低频操作场景里乐观更新的冲突概率极低但代码简单且不占用数据库连接。注意Transactional必须加否则更新状态和后续的下发日志写入不在同一个事务里一旦后续步骤失败状态已经变成“待执行”而设备没有收到指令数据就不一致了。下一次要再下发时判断“只有草稿能下发”就会把任务卡死操作人员只能手工改库这种情况我遇到过不止一次。状态机的status字段用字符串字典值而非数字还有一个额外的好处数据库里直接查数据时一眼能看懂任务在哪个环节排查问题不用再翻代码找 1、2、3 分别代表什么。4. 用若依代码生成器落地巡检模块生成源码的改造与异步回写接口4.1 生成器源码必改三处包名、时间字段、自定义接口若依的代码生成器是这套框架里性价比最高的功能。在菜单栏的“代码生成”里导入刚才建好的表配置好包名、模块名、业务名就能下载生成一套包含 Domain、Mapper、Service、Controller、Vue 页面的前后端分离项目实战产物。但生成器生成的是标准单表 CRUD直接搬到巡检业务里会有三个明显的坎。第一是包名扫描问题。生成器默认的父包名往往和主启动类不在同一个包路径下如果把生成代码单独扔进新建的 module启动类扫描不到运行时报 Bean 不存在。我一般会在生成时就把包名改成和主启动类一致的根包比如com.company.patrol并在启动类上保持默认扫描路径不做特殊配置避免给自己埋坑。若依框架在多模块场景下的这个约束是新手最容易卡住的地方。第二是时间字段的处理。生成器会为create_time、update_time生成自动填充逻辑但plan_start_time、actual_start_time这种业务时间字段也是 datetime 类型生成器会把它当成创建时间处理导致前端传值被覆盖。需要在 Domain 类里检查这些字段的JsonFormat注解和自动填充注解是否正确。第三是自定义接口。生成器只提供“增删改查”任务下发、状态流转、文件回传这些接口必须手写。我的做法是保留生成器产出的标准接口在同一个 Controller 里新增业务接口而不是新建一个 Controller这样前端调用的路径统一代码维护时也不用来回切文件。4.2 回传接口接收文件只做落盘耗时识别交给异步线程无人机巡检回传数据的场景是机场把飞行过程中拍摄的照片打包回传后端需要保存文件并触发 AI 识别。最直观的写法是把文件落地、缩略图生成、AI 识别全都放在 Controller 方法里同步执行但这样做会在高峰期直接把 Tomcat 线程池打满。巡检数据的特点是单次回传文件多、单文件体积大回传接口耗时不可控。我用的方案是 Controller 只做接收和快速落盘后续处理交给异步线程。先看回传接口的简化代码RestController RequestMapping(/patrol/record) public class PatrolRecordController extends BaseController { Autowired private PatrolRecordService patrolRecordService; PostMapping(/upload) public AjaxResult upload( RequestParam(taskNo) String taskNo, RequestParam(deviceNo) String deviceNo, RequestPart(files) MultipartFile[] files) { // 1. 先把上传文件转存到本地临时目录 ListString filePaths new ArrayList(); for (MultipartFile file : files) { String path /data/patrol/tmp/ taskNo / System.currentTimeMillis() _ file.getOriginalFilename(); try { file.transferTo(new File(path)); filePaths.add(path); } catch (IOException e) { // 落盘失败直接返回错误调用方可以重试 return error(文件保存失败: e.getMessage()); } } // 2. 文件落盘成功后再触发后续异步处理 patrolRecordService.processRecord(taskNo, deviceNo, filePaths); return success(); } }注意这里关键的一步我没有把MultipartFile直接传给异步方法而是先同步transferTo转存成文件路径。原因是 Spring 的MultipartFile底层依赖当前的 HTTP request 生命周期一旦 Controller 方法返回request 被回收异步线程里再读文件内容会报IllegalStateException。先落盘再异步是这类回传接口的固定套路。异步处理方法的代码大致如下Async(patrolExecutor) public void processRecord(String taskNo, String deviceNo, ListString filePaths) { // 1. 写入巡检记录表状态置为 RETURNED PatrolRecord record new PatrolRecord(); record.setTaskNo(taskNo); record.setDeviceNo(deviceNo); record.setFilePaths(String.join(,, filePaths)); patrolRecordMapper.insert(record); patrolTaskService.updateStatusByTaskNo(taskNo, EXECUTING, RETURNED); // 2. 触发AI识别这里是远程调用推理服务 try { ListDefectResult defects aiClient.analyze(filePaths); // 3. 识别结果写入缺陷工单表 defectOrderService.saveDefects(record.getRecordId(), defects); // 4. 更新任务状态为 FINISHED patrolTaskService.updateStatusByTaskNo(taskNo, RETURNED, FINISHED); } catch (Exception e) { log.error(AI识别失败 taskNo{}, taskNo, e); // 失败时任务状态回滚到 RETURNED下次可以重试 patrolTaskService.updateStatusByTaskNo(taskNo, ANALYZING, RETURNED); } }线程池要单独配置不要和若依默认的线程池混用。我用 Spring 的ThreadPoolTaskExecutor单独定义了一个patrolExecutor核心线程数按设备数量估算比如 10 台机场同时回传核心线程给 20队列容量 200。参数设置过小的后果是回传请求处理来了但异步任务排队用户端看到的状态长时间不更新容易误以为系统卡死。AI 识别失败时把任务状态回滚到 RETURNED是为了让整个任务流转可以重试把这个逻辑写清楚后就不会出现失败后任务卡在“分析中”的尴尬局面。5. 若依做巡检后端最容易翻车的 5 个坑现象、原因、解决5.1 生成代码放不进新模块改了包名还是扫不到 Bean现象在 IDEA 里把若依代码生成器生成的源码复制到新建的patrol-module模块里启动项目时 Controller 访问 404日志里报PatrolTaskServiceBean 不存在。原因Spring Boot 的启动类默认扫描启动类所在包及其子包。主启动类在com.company.admin新模块的代码放在com.company.patrol两个包平级且不在同一棵子树下扫描路径覆盖不到。若依框架默认的SpringBootApplication注解没有配置scanBasePackages所以新模块永远进不了 Spring 容器。解决两个方案二选一。一是在主启动类的SpringBootApplication注解上明确指定扫描根包写scanBasePackages com.company让所有子包都能被扫到。二是把生成代码的包名改成主启动类所在包路径下比如com.company.patrol直接放在com.company下。第二种做法改动最小几乎所有用若依做多模块的团队最后都统一到这种方式。5.2 任务状态用字符串裸写后期排错全靠肉眼现象需求方提出要增加“任务暂停”状态开发改完状态判断后旧数据里有一些任务的status字段值是手动写进数据库的PAUSE但代码枚举里没有这个值列表页筛选直接查不出数据。原因代码里到处是if (EXECUTING.equals(task.getStatus()))这种硬编码没有统一的状态枚举类数据库里也允许任意字符串写入。解决先在若依字典里维护一套任务状态字典代码里新建一个PatrolTaskStatus常量类或枚举类把所有状态值集中定义。数据库层面加上CHECK约束或者用字典表做外键约束不现实但至少要在 Service 层的写入入口统一校验状态值是否合法。我的习惯是枚举类和字典值的命名完全一致这样前端下拉显示、后端校验、数据库存储三层共用同一套值排错时不需要在代码和字典之间翻译。5.3 大图上传把服务压垮耗时逻辑全部跑在 Controller 线程里现象巡检图片回传时单次请求里有几十张 20MB 以上的照片上传接口在高峰期响应时间从 500ms 涨到 60 秒Tomcat 默认 200 个工作线程被占满整个后端页面的登录和菜单请求也全部卡住。原因回传接口里同步做了文件转存、缩略图生成、AI 识别、结果入库四件事。Tomcat 线程被慢请求占住后其他所有请求都在排队看起来就是系统整体假死。解决把 Controller 里的业务逻辑拆成“快速落盘 异步处理”两步就是第 4 章展示的写法。同时给异步线程池设置合理的队列容量和拒绝策略队列满了以后不再接收新的异步任务而是直接抛出异常让调用方感知避免任务无限堆积把内存耗尽。这个坑在巡检项目里几乎是必踩的因为无人机机场回传的数据量和普通业务系统完全不是一个量级。5.4 跨部门数据权限执行人员看不到任务也传不了数据现象项目部在系统里创建巡检任务并下发外业执行组的账号登录后在任务列表里看不到刚下发的任务外业人员手动输入任务编号回传图片时提示“数据不存在”。原因若依的数据权限注解DataScope默认按创建人的部门过滤数据。项目的创建部门是“项目部”执行人员属于“外业部”patrol_task表里的create_by是项目部账号数据权限过滤后外业部账号查不到这条任务。解决在任务表里增加一个dept_id字段记录执行部门的归属查询时用DataScope(deptAlias t)关联任务表的dept_id而不是默认的create_by部门。上传回传接口查询任务时只按task_no精确匹配不走数据权限过滤因为回传数据的操作者已经由登录身份做了认证。跨部门协作的巡检场景一定要在表设计阶段就想清楚数据归属否则上线后再改数据权限规则牵扯面非常大。5.5 定时扫描任务漏数据Quartz 日志显示成功但任务没下发现象若依定时任务页面配置了“每半小时扫描待下发任务”调度日志每次都显示执行成功但数据库里计划到时间该下发的任务仍然停在待执行状态第二天人工检查才发现漏了。原因扫描逻辑里先查出一批待下发任务再循环调用下发方法。下发方法内部做了状态乐观更新如果两个定时任务在同一时刻触发或者扫描逻辑里没有加事务前一次查询拿到的是旧状态循环到某条任务时update ... where status旧状态影响行数为 0异常被 catch 后吞掉了只打了日志。Quartz 的调度日志只记录方法是否抛异常不会记录内部吞掉的业务失败。解决扫描方法加上Transactional查询待下发任务时用SELECT ... FOR UPDATE锁定这批行避免同一任务被两个线程同时读到。循环内不允许 catch 后不处理所有业务异常都重新抛出让若依的调度日志记录完整堆栈。另外在扫描方法里加一个简单的调用计数执行完以后比对“应下发数量”和“实际下发成功数量”不一致时发告警消息避免漏数据只能靠第二天人工发现。6. 验证与进阶用模拟数据把巡检后端跑起来再把 AI 回写做成契约6.1 造数据一个脚本生成一周的模拟巡检任务验证状态流转最直接的办法是灌一批模拟任务。下面这个 Python 脚本可以向数据库插入一周的巡检任务数据覆盖不同状态方便你快速看到列表和状态流。import pymysql import random from datetime import datetime, timedelta conn pymysql.connect(host127.0.0.1, userroot, password123456, databaseruoyi, charsetutf8mb4) cursor conn.cursor() task_status [DRAFT, PENDING, EXECUTING, RETURNED, FINISHED] start datetime.now() - timedelta(days7) for i in range(50): task_no fSIM-{start.strftime(%Y%m%d)}-{i:03d} status random.choice(task_status) sql INSERT INTO patrol_task(task_no, task_name, device_id, status, plan_start_time, create_by, create_time) VALUES(%s, %s, %s, %s, %s, admin, %s) cursor.execute(sql, (task_no, f模拟任务{i}, random.randint(1, 10), status, start timedelta(hoursrandom.randint(0, 48)), datetime.now())) conn.commit() cursor.close() conn.close()运行前先在device表里插入几条设备数据device_id要和脚本里的随机数对应上。插入完成后你可以在若依的巡检任务列表页看到各个状态的任务点击下发、归档按钮观察状态是否按预期流转这比用单元测试更直观。6.2 压测任务下发接口用 JMeter 命令行出一个报告任务下发接口的并发表现直接关系到多台无人机同时作业时会不会出乱子。我习惯用 JMeter 的命令行模式跑一轮简单压测jmeter -n -t patrol_task_dispatch.jmx -l result.jtl -e -o report/-n表示非 GUI 模式-t指定测试脚本-l输出原始结果文件-e -o生成 HTML 报告。测试脚本里配置 50 个线程循环 20 次观察聚合报告里的响应时间 P95 和错误率。做这个验证的重点不是追求高 TPS而是确认状态乐观更新在并发下不会产生重复下发压测完去数据库查一下patrol_task表里有没有同一条任务被下发两次的记录。6.3 进阶把 AI 识别结果回写做成固定契约无人机巡检后端上线后大概率要对接独立的视觉识别服务。我的做法是在后端定义一份固定的回写契约让识别服务按这个结构推送结果而不是把识别逻辑写进巡检后端。接口的 JSON 结构简化如下{ taskNo: 20250601-001, pointIndex: 12, defects: [ { type: BIRD_NEST, level: HIGH, aiScore: 0.93, imagePath: /patrol/2025/0601/xxx.jpg } ] }契约里要特别注意imagePath使用相对路径后端再拼接文件服务的地址前缀。这样后续对象存储迁移或换域名时历史数据不用批量改库。生产环境的数据库连接用的是若依默认的 Druid 连接池项目上线前我会把数据库密码做加密配置不把明文密码留在application-druid.yml里。我自己做巡检项目时养成的习惯是先拿一张纸把任务状态图画出来再动手建表写代码。状态图画清楚后面的接口和页面都顺了返工最少。希望帮到你。本文还有配套的精品资源点击获取
返回列表