☰
船舶维保管理系统开发:SpringBoot+Vue实现工单与备件闭环
2026/10/21 9:51:43 网站建设 项目流程

船舶维保这块,我一直觉得是管理软件里被低估的一个方向。真正跑过船、管过设备的人都知道,维保不是“按时换机油”这么简单,它牵扯到船舶检验周期、备件库存、船员排班、工单闭环、证书有效期一串链条。这两年基于SpringBoot+Vue的船舶维保管理系统源码项目特别多,但很多只是把CRUD套了一层“船舶”的皮,真正能落地的没几个。这篇文章我想围绕这类系统,把从需求拆解、表结构设计到工单流转、备件联动的完整思路捋一遍,顺便聊聊我踩过的坑。

1. 项目背景与需求拆解

1.1 船舶维保行业的普遍痛点

先说说我为什么会花力气研究这类系统。船舶维保和普通工业设备维保有明显差异:船舶是移动的资产,维保工作分散在航次间隙、锚地停泊、船厂检修等多个场景,现场环境复杂,网络条件差,参与角色多——岸基管理人员、船长、轮机长、普通船员都要在流程里扮演不同角色。

在实际项目中,最常遇到的痛点有三个。第一个是计划与实际脱节,很多船管公司用Excel排维保计划,但船舶航线一变、船员一换,计划就没人管了,真正的维保做没做全凭自觉。第二个是备件库存不清,备件买回来放哪个船、入库多少、领用给谁,靠手写台账根本对不上,经常出现“账上有货但库里找不到”“紧急采购回来发现库房还有两个”的情况。第三个是记录不闭环,做完维保没有标准记录,等检验机构上船检查要维保证据时,拿出来的是一堆纸面单据,既难查也难信。

这套系统要解决的,本质上是三个问题:怎么把维保计划排得科学又能自动跟进,怎么把备件和工单联动起来做到消耗可追溯,怎么让岸基实时看到每艘船的维保状态。解决好这三点,系统就有真实价值,而不是一个展示用的Demo。

1.2 核心角色与业务流程梳理

在动手设计表结构之前,我建议任何做这类项目的人都先画一张角色-流程图(用文档工具画即可,不一定要上建模软件)。

系统的核心角色有四类:

  • 岸基管理员:负责制定年度维保计划、审批工单、查看统计报表、管理基础数据。
  • 船长/船舶负责人:查看本船的维保计划,确认工单执行情况,反馈执行结果。
  • 轮机长/大副:实际执行维保工单,录入完成情况,申请备件领用。
  • 备件管理员:处理备件入库、出库、盘点,维护备件安全库存。

核心业务流程可以描述为一个闭环:岸基管理员根据设备保养周期和船舶检验计划生成维保计划,系统按时间节点生成待办工单,船舶端登录后领取工单,执行完成后填反馈(工时、备件消耗、遗留问题),岸基审核后归档,同时备件库存相应扣减,最后所有数据汇总到统计报表。

这里最容易忽略的是“检验周期”和“维保周期”的关联。船舶有年度检验、中间检验、换证检验等强制节点,很多设备(比如救生设备、消防设备)的维保周期直接和检验周期挂钩。如果能把检验日期作为维保计划的触发条件之一,系统会更贴合行业实际。

1.3 功能模块划分与优先级

基于上面的角色和流程,系统的功能模块我通常拆成六大块:

模块核心功能优先级
船舶档案船舶基础信息、证书管理、设备台账P0
维保计划保养标准库、周期计划生成、计划调整P0
工单管理工单下发、认领、执行、审核、归档P0
备件管理备件档案、出入库、库存预警、领用关联P1
报表统计维保完成率、工时统计、备件消耗统计P1
系统管理用户、角色、菜单、日志P0

P0的模块是系统能正常跑起来的基础,P1模块决定系统是否好用。备件管理和工单管理的联动是这类系统的灵魂,如果只做独立的备件CRUD而没有和工单关联,那备件模块就废了。

2. 技术选型与架构设计

2.1 为什么选SpringBoot+Vue+MyBatis+MySQL

这个组合在2025年依然是中小型管理系统最稳妥的搭配,不用追新,但也不是守旧。

后端用SpringBoot的理由很明确:生态成熟、内置Tomcat、自动配置能力强,单体应用几十张表的情况下开发效率极高。SpringBoot里最实用的就是starter机制,引入一个依赖就能少写一堆配置类,配合application.yml做集中配置,扩展起来也很方便。对于船舶维保这种业务逻辑清晰、并发量不高的系统,SpringBoot完全够用,硬上微服务纯粹是给自己找麻烦。

MyBatis在这个项目里的地位比较特殊。维保系统的查询需求极度依赖多表关联和条件动态拼接,比如“查某艘船某个日期范围内所有未完成工单涉及的备件列表”这种查询,MyBatis的动态SQL写起来最顺手。相比JPA,MyBatis对SQL的控制力更强,算法人员调起SQL也更有数;相比MyBatis-Plus,虽然Plus能减少大量单表CRUD代码,但如果项目里有一些复杂的报表SQL,原生MyBatis依然灵活。稳妥的做法是:单表操作用MyBatis-Plus,复杂查询用原生SQL,两者共存。

前端选Vue不用多说,Vue的响应式机制和组件化开发非常适合这种表单密集型系统。版本上建议Vue 3 + Element Plus,组件全、文档丰富、社区帖子多。Vue 2虽然还有存量项目在用,但新项目没必要为旧版本绑住手脚。

MySQL是这套组合的地基,InnoDB引擎、事务支持、行级锁,满足系统数据一致性要求。维保工单的审批流转、备件出入库的库存扣减,这些操作都依赖事务,MySQL在这方面的表现稳定可靠。版本建议8.0以上,窗口函数和JSON能力在写统计报表时很实用。

2.2 后端分层与包结构

后端工程结构直接决定项目后期维护成本。我见过很多源码项目把Controller、Service、Mapper文件堆在一个包下,表一多就乱成一锅粥,这里分享一套我在船舶维保系统里实际使用的包结构:

com.ship.maintain ├── config // 配置类:跨域、拦截器、MyBatis-Plus配置 ├── controller // 控制器层:接收请求、参数校验、返回统一响应 ├── service // 业务层:核心业务逻辑、事务控制 │ └── impl ├── mapper // 数据访问层:接口 + XML ├── entity // 数据库实体类 ├── dto // 请求/响应对象,避免实体直接暴露 ├── vo // 视图对象,报表专用 ├── common // 通用类:统一返回结果、异常处理、常量 ├── utils // 工具类:日期处理、Excel导出等 └── job // 定时任务:自动生成工单、库存预警等

分层的关键不是包名有多规范,而是依赖方向要正确:Controller只调Service,Service只调Mapper接口,不能出现Controller直接操作Mapper的情况。另外还要规定实体的使用边界——数据库实体类(entity)不能直接返回给前端,必须转成VO或DTO。原因很现实:实体类里的createTime、updateTime、deleted这些字段,前端根本不需要看到,直接暴露还会带来安全隐患。

2.3 前端工程结构与基础封装

前端工程用Vite + Vue 3脚手架生成,项目结构同样值得用心规划:

src ├── api // 接口定义模块,按业务域拆分 ├── components // 通用组件:表单弹窗、分页表格、文件上传 ├── views // 页面级组件:船舶管理、维保计划、工单等 ├── router // 路由配置 + 动态路由 ├── stores // 状态管理(Pinia) ├── utils // axios封装、格式化工具 └── layout // 主布局:侧边栏 + 顶栏 + 内容区

axios封装是我特别想强调的部分。很多半成品源码项目的axios封装就一个get和post,没有统一拦截器,每个请求都要单独写错误处理,代码冗余且乱。建议封装时至少做好三件事:统一在拦截器里附加token、统一处理后端返回的code并弹出错误提示、统一处理HTTP 401跳转登录页。这三件事做好,前端联调时的体验会有质的提升。

3. 数据库设计:船舶维保系统的根基

3.1 核心表结构设计与关系梳理

数据库设计是整个系统的根基,表结构定下来,后面的开发基本就是往里面填业务。船舶维保系统我核心设计了8张表,这里拆开讲每张表的用途和设计要点。

ship_info(船舶档案表):存储船舶基本信息——船名、IMO编号、船型、总吨、航区、建造日期等。IMO编号要设置唯一索引,这是国际通用的船舶识别码,后续所有业务表都通过ship_id关联到这个表。证书信息我建议单独拆一个ship_certificate表,因为证书有有效期,需要单独做到期提醒,放在ship_info里字段会冗余且难以查询。

device_info(设备台账表):船舶上的设备清单——主机、辅机、锅炉、舵机、锚机、压载水系统、消防设备、救生设备等。每台设备归属于一艘船,维护保养周期(比如“每250小时更换滑油滤芯”)、上次维保时间、下次维保建议时间。device_info里的maintenance_cycle字段是维保计划自动生成的直接依据,建议用“周期数值+周期单位”两个字段,比如cycle_value=250、cycle_unit=hour,而不是写一个字符串“250小时”,否则后期做计划运算时还要解析字符串,很痛苦。

maintenance_plan(维保计划表):按船舶+设备生成的计划,包含计划名称、计划类型(日常保养/定期保养/检验关联保养)、计划开始日期、计划截止日期、执行状态、关联的工单ID。一张计划可以对应多个设备,也可以用一张plan_detail表拆开,但如果设备数量不多,直接在设计时预留一个device_ids字段(逗号分隔)也行。我一般建议正规一点拆plan和plan_item,毕竟多表设计对后续按设备维度做统计更友好。

maintenance_order(维保工单表):工单是维保质量的核心载体,字段包括工单编号(唯一业务单号)、关联计划ID、船舶ID、指派班组/负责人、工单状态(待领取/执行中/待审核/已归档/已取消)、优先级(普通/紧急/加急)、期望完成时间、实际完成时间、审核意见。工单状态是整个系统流转的核心状态机,我后面的章节会展开讲。

maintenance_record(维保执行记录表):记录工单执行时的详细信息——工时、参与人员、操作内容、更换了哪些备件、遗留问题、图片附件。这张表是后期做追溯和统计的主要数据源。维保记录和工单可以是一对一的关系,也可以在工单下多次补充记录,做成一对多会灵活一些,但查询时要多注意。

spare_part(备件档案表):备件名称、型号、适用设备类型、供应商、安全库存、当前库存。备件档案一定要关联设备类型,而不是直接关联设备实例,因为同一型号的备件可能适用于多艘船的多台设备,直接关联设备会让库存数据分裂。

stock_record(出入库记录表):记录备件的入库、出库、退库、盘点调整操作,每个操作包含操作类型、数量、关联工单ID(如果是工单领用)、操作人、操作时间。这张表库存扣减的事务操作就是在这里完成的,建议所有对库存数值的修改都必须经过这张表留痕,不允许直接UPDATE spare_part的current_stock字段。

sys_user + sys_role + sys_menu(系统管理三件套):用户表带船舶权限字段,表示这个用户能看哪些船的维保数据。角色表区分岸基管理员、船端用户、备件管理员。菜单表用来做动态路由和按钮级权限。

3.2 关键业务表字段详解:工单表的隐藏设计

工单表设计时有一个隐藏要点:状态流转历史。很多系统只在表里放一个status字段,今天改成“已归档”后,看不出之前是从“待领取”改过来的还是从“执行中”改过来的。这个在项目验收或者出现问题时,完全说不清。我建议加一张order_status_log表,每次状态变更都插入一条记录,内容包括工单ID、变更前状态、变更后状态、操作人、操作时间、操作备注。这个设计前面积累的数据,后期做效率分析时会很有价值。

另外一个要注意的字段是期望完成时间的动态调整。船舶维保工单常常因为天气、航线更改而延迟,如果期望完成时间只允许创建时填写,系统就会产生大量过期待办却无法调整,反而失真。处理方式是在工单主表里加一个current_deadline字段,延期申请通过后更新这个字段,而原始期望时间存到create_deadline字段,两个字段对比能反映出延期了多少次、延了多久,方便考核维保计划的执行率。

状态机设计是工单模块的核心逻辑,我定义的状态转换如下:

  • 待领取 → 执行中(船员领取工单)
  • 执行中 → 待审核(船员提交完成反馈)
  • 待审核 → 已归档(岸基审核通过)
  • 待审核 → 执行中(岸基打回,补充信息)
  • 待领取 → 已取消(岸基取消或计划调整)
  • 执行中 → 已取消(特殊情况终止,需备注原因)

状态转换的校验应该写在Service层,而不是靠前端控制,因为接口可以被直接调用,前端隐藏按钮拦不住人。

3.3 索引设计与查询性能考虑

索引设计在船舶维保这种“中等数据量”的系统里,反而容易被忽视。数据量不大,随便查也就几万条,所以很多开发就直接不建索引。但维保系统有个特点:报表查询往往要全表范围扫,例如跨年度统计一台设备的维保次数、某段时间内所有船的备件消耗汇总,这些查询如果表扫描几万行加上动态JOIN,响应时间就会很不体面。

我建索引的原则有三个:第一,所有外键字段都要建索引,ship_info.id、device_info.id、maintenance_plan.id在关联表里作为外键出现时,必须有索引;第二,业务上常用查询条件要建联合索引,比如maintenance_order表的(ship_id, status),因为“某艘船的未完成工单”是最高频查询;第三,状态字段加上创建时间字段建联合索引,如(status, create_time),支撑“按状态+时间范围做统计”的场景。

另外,报表查询里用到的日期字段建议单独建索引。我踩过一个坑:统计某个月完成的工单数时,SQL里写了WHERE finish_time BETWEEN '2025-01-01' AND '2025-02-01',但finish_time没有索引,全表扫了1.8秒,加完索引直接降到30毫秒。

4. 核心功能模块实现详解

4.1 维保计划生成:周期与规则引擎

维保计划是整个系统的核心逻辑,前端页面再漂亮,后台计划生成不合理,系统就没有实用价值。计划生成有两种思路:简单方案是遍历所有设备的维护周期,按周期一次性生成未来一年的计划;进阶方案是引入“运行时长触发”机制,比如主机运行每2500小时需要保养,这种就不能只按日期生成,要结合船舶的航次记录估算运营时长。

对于大多数船舶维保系统源码项目,按日期周期生成已经能满足需求。我在这里给出一个按日期间隔生成计划的算法逻辑:

  1. 从device_info表取出所有启用的设备记录。
  2. 计算每台设备的下次维保日期:上次维保日期 + 维护周期数值(按周期单位换算成天数)。
  3. 如果下次维保日期在计划窗口内(如未来30天),则生成一条maintenance_plan记录。
  4. 计划生成时按时间窗口(周/月)批量创建工单,而不是每天跑。
  5. 工单生成后,发送站内通知或短信提醒船舶端。

这里有一个容易犯的错误:周期字段的换算不能想当然。周期单位有可能是小时、天、周、月,直接把“12个月”解析成“365天”会导致每年差几天,时间长了计划就漂移了。我在设计时会让设备台账里存两个字段:cycle_value和cycle_unit,周期换算写成一个工厂类,计算日期时严格按Calendar或LocalDate的plusMonths/plusWeeks来做,保证精度。

计划调整的坑:现实中设备维保周期可能因船况、厂家建议、检验机构要求而调整。如果每调整一次就改device_info的周期字段,那此前生成的计划全部作废。我建议在maintenance_plan表里增加一个plan_source字段,标记这条计划是“自动生成”还是“手动调整”,手动调整的计划优先级高于自动计划,自动计划在下次生成时可以跳过已被手动计划覆盖的设备,防止重复生成。

4.2 工单闭环流转实现:从待领取到已归档

工单流转是系统使用频率最高的核心功能,用户每天的操作基本上就是“领取工单→填写反馈→提交审核→查看审核结果”。这个业务逻辑听起来简单,但实现时细节很多。

领取操作:船员登录系统后,在“我的工单”列表看到分配给本船的待领取工单。领取时后端要做两个校验:一是校验工单状态确实是“待领取”,防止两个船员同时点击领取造成并发冲突;二是校验操作人是否属于该船舶的关联用户。并发冲突的解法是用乐观锁,在工单表加version字段,UPDATE语句里带上WHERE status='待领取' AND version=?,更新成功后version自增。如果更新影响行数为0,说明别人已经把单子领了,直接提示“该工单已被其他用户领取”。

执行反馈:船员执行完维保任务后,填写反馈,核心包含三部分内容:实际维保内容文字描述、更换备件明细(通过选择备件档案的操作,自动生成出库单并扣减库存)、上传现场照片附件。这里备件领用的数据一致性处理是系统的一个关键部分,要放在同一个事务里。如果先保存工单记录再扣库存,中间发生异常就会数据不一致。正确做法是:在Service方法上标注@Transactional,方法内依次写维保记录、生成备件出库单、扣减备件库存、更新工单状态,任何一个步骤失败则整体回滚。

审核操作:岸基管理员看到待审核的工单后,查看反馈内容和照片,选择通过或打回。通过后工单状态变为已归档,同时更新对应设备的last_maintain_time,为下一次计划生成提供数据。打回时必须填写原因,原因会推送给船端用户,船端用户修改后重新提交。

这个闭环逻辑我建议做成一张流程图放在项目文档里,让新接手的开发一眼看懂状态流转,比读代码快得多。

4.3 备件库存模块:事务、库存扣减与预警

备件库存模块的账面数据准确性直接影响船舶日常运营。船在海上运行时,备件不够用不是小事,所以这个模块我宁可做得笨一点,也要保证数据可靠。

出入库设计:所有出库操作必须关联一张业务单据。工单领用出库关联maintenance_order表,其他出库(如送检、报废)关联一个手动出库单。入库操作对应采购到货或者退货,关联采购单。每一笔操作写入stock_record表,库存数值的变更只允许通过stock_record这张流水表驱动。

核心更新语句一般是这样的:

UPDATE spare_part SET current_stock = current_stock - #{quantity} WHERE id = #{partId} AND current_stock >= #{quantity}

注意这里加了current_stock >= #{quantity}条件,利用数据库的行锁和条件更新,避免超卖情况。如果影响行数为0,说明库存不足,业务层抛出异常并回滚事务。

安全库存预警:在spare_part表设计时设置了safety_stock字段,库存扣减事务完成后,检查current_stock是否低于safety_stock,如果是则插入一条预警记录,并且在前端顶部导航栏的提醒气泡中显示出来。预防性预警我建议做一个定时任务,每天凌晨跑一次全量检查,防止漏掉事务中的预警分支,双保险。

盘点功能:我在后几个项目中给备件模块加了盘点单功能。盘点时生成盘点单,录入实物数量后和系统库存做差异比对,差异数自动生成“盘盈入库”或“盘亏出库”的调整记录。这个功能平时用不上,但年底审计时救了大命,没有盘点单的库存系统,账实不符时根本没法调整。

4.4 数据报表:维保完成率与备件消耗分析

报表模块是船舶维保系统的“成果展示页”,也是岸基管理层最关心的部分。用MyBatis写统计SQL时,我总结出三个实用的写法。

维保完成率:统计某段时间内应完成的工单数和实际完成的工单数。应完成数来自maintenance_plan表,统计截止日期在当前时间段内的计划数;实际完成数来自maintenance_order表,统计已归档且完成时间在时间段内的工单数。两个数一除就是完成率。这里要注意“应完成”的口径,如果计划截止日期在统计时间段之后,那还没有到期,不能算入应完成数量,否则完

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询