ARTICLE DETAIL

资讯详情

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

基于SpringBoot的小型船舶进出港登记系统设计与实现

基于SpringBoot的小型船舶进出港登记系统设计与实现 做毕设选题的时候很多同学都挤在电商系统、图书管理系统、校园考勤系统这些“经典题目”上。倒不是说这些不能做而是同质化太严重答辩现场十个人有七个做图书管理评委听到题目基本就预判了你的架构和数据库长什么样。今天我想把一个性价比很高的毕设方向拆开讲透——基于SpringBoot的小型船舶进出港登记系统。这个系统到底做什么你可以理解成海事部门用来管船舶进港出港的一套数字化台账。过去小型船舶进出港口靠纸质单子登记船主填表、值班员手工核对数据散在纸面上想查某条船什么时候进过港、运了什么货、证书有没有过期翻半天台账都找不全。现在把整个流程搬上线船主在线提交进出港申报海事值班员审核通过或驳回进出港时间、货物、船员信息全部留痕系统还能自动出报表、提醒证书到期。它解决的核心问题就是把“人盯人、纸对纸”的监管模式改成“数据自动流转”。这个题目适合计算机类毕设的原因很实在业务有辨识度不会跟满大街的图书管理系统撞车技术覆盖面够广SpringBoot、MyBatis-Plus、Redis、JWT、文件上传都能放进去数据模型又有层次船舶、船员、证书、申报单之间关系足够撑起一份漂亮的数据库设计。下面我把思路、选型、表结构、核心代码、踩坑点一次说清楚。1. 项目定位与需求拆解1.1 先捋清业务再谈怎么写代码我见过太多同学卡在技术细节上页面写了一半发现业务对不上回头疯狂改表。船舶进出港登记这个业务表面看是“登记”实际是一条完整的状态流转线。一条船要进港船方得先提交进港申报船叫什么名字、从哪个港口来、船上运了什么货、装了多少吨、船员有哪几位。海事值班员在系统里看到这条申报核验船舶证书是否有效、船员适任证书是否齐全没问题就审核通过船舶才能靠泊作业。出港同理只是信息变成去哪个港口、手续是否结清。所以做这个小项目时你脑子里要时刻记住一个核心对象申报单。船舶档案是基础数据船员和证书是辅助数据所有业务动作最终都围绕申报单发生。我帮同学捋需求的时候第一件事就是画这条线申报单从草稿到待审核再到通过或者驳回最后到归档。整个过程只有四个状态但把所有功能模块串了起来。理解了这一点数据库设计和模块划分就不会跑偏。你如果一上来就跟着感觉做“增删改查模块”做到一半就会发现没有状态流转的登记系统只是一个空壳。1.2 用户角色与权限边界怎么划系统至少要有三类角色建议先按这个来设计权限既能讲清楚又不会把权限模块做成一个无底洞。第一类是船方用户登录后看到的是船舶管理、船员管理和进出港申报能提交申报单、查看自己的历史记录、编辑草稿。第二类是海事审核员负责处理船方提交的申报单可以审核通过或者驳回也能查看统计报表。第三类是系统管理员不直接参与业务只管用户分配、角色配置、菜单权限和数据字典维护。权限控制不一定要上Spring Security那套复杂过滤器链。我建议做JWT登录认证再写一个HandlerInterceptor拦截器从token里解析出用户角色在请求到达Controller之前判断当前角色能不能访问这个接口。这样一来代码量适中中间的过程又足够你在答辩时讲清楚。哪怕答辩老师追问“你的权限是怎么实现的”你从token生成、登录校验、注解标注、拦截器校验这条链路往下讲逻辑非常清晰比笼统地说“用了Spring Security”要扎实得多。1.3 功能模块全览把系统拆成模块看大概是这样模块核心功能面向角色系统管理用户管理、角色管理、菜单管理、字典管理管理员船舶管理船舶档案增删改查、证书上传、证书到期提醒船方、审核员船员管理船员信息录入、适任证书管理船方、审核员进出港申报进港申报、出港申报、申报记录查询船方审核管理待审核列表、审核通过、审核驳回、审核历史审核员统计报表进出港趋势、船舶类型占比、月度统计审核员、管理员这里有个点很多同学容易漏船舶证书是有有效期的比如适航证书每年要检验一次到期了船就不能正常出港。这块可以加一个定时任务每天扫一遍船舶证书表把30天内到期的船舶找出来推送到审核员的消息列表或者在列表查询时标注“证书即将到期”。这个功能写起来不难但特别能体现业务理解论文里可以作为一个特色来写答辩时也是一个不错的加分故事。2. 技术选型与架构设计2.1 SpringBoot版本到底怎么选聊技术先从最让人纠结的版本说起。做毕设不要追新好用和稳定比什么都重要。如果你的电脑装的是JDK 8那就选SpringBoot 2.7.18这两个搭配非常成熟网上教程、博客、报错答案一搜一大把基本没有查不到的问题。如果你已经装了JDK 17那就选SpringBoot 3.2.x也能做但要注意3.x把很多底层实现改成了Jakarta命名空间部分依赖版本对不上会报错。我个人建议能选2.7就选2.7别给自己添堵。版本选择背后还有一个隐藏逻辑是兼容性。SpringBoot 2.7搭配MySQL 8.0驱动、MyBatis-Plus 3.5.x完全没问题但如果你硬要在SpringBoot 3.2下面挂MyBatis-Plus 3.4.0大概率会撞上依赖冲突。做项目前先确认一组能跑通的版本组合把这些写进pom文件里就不动了。不要今天看到一个教程说用这个版本明天又换一个换来换去浪费的时间比写代码还多。2.2 配套组件选型SpringBoot只是底座周边组件我推荐这么一套搭配。第一个是MyBatis-Plus。它能把单表CRUD、分页查询、逻辑删除全部封装好省下大量重复劳动。毕设时间本来就紧完全没必要手写二十遍“根据ID查询、新增一条数据”。第二个是Redis。用来存登录token和缓存字典数据因为JWT本身无状态但你如果希望用户退出登录后token立刻失效就得把token存Redis退出登录时删掉。第三个是JWT负责接口访问凭证。第四个是ECharts做统计报表的前端展示。第五个是文件上传船舶照片、证书扫描件用本地磁盘存储就够配置一个上传目录再映射成静态资源访问路径想加分的同学可以引入MinIO但如果你只有一台电脑、不想折腾额外部署本地存储完全够用。至于要不要加Activiti或者Flowable这类工作流引擎我的看法是明确不要加。进出港申报的审核只有“提交、通过、驳回”这么一条直线工作流引擎是杀鸡用牛刀还会引入一堆你答辩时未必解释得清楚的概念。一个状态字段就能说明白的事情不要在毕业设计里把自己绕进复杂框架。2.3 单体架构还是前后端分离这个取决于你擅长什么。如果你对Vue比较熟或者愿意花一点时间学基础那就做前后端分离SpringBoot只提供JSON接口前端用Vue3加Element-Plus写页面开发调试和答辩演示都很流畅。如果你前端基础薄弱不想在CSS和组件上消耗太多精力那就在SpringBoot里直接集成Thymeleaf模板引擎后端渲染页面一套代码全搞定。两种方案都能拿高分关键是选你有把握的那条路。从论文写作角度前后端分离的优势是技术栈内容更丰富可以多写跨域处理、接口设计、前端路由这些东西单体架构的优势是结构简单内容更聚焦于SpringBoot本身。下面我会以后端为核心来讲前端具体用哪种框架你可以按自己的基础决定。核心业务逻辑和数据库设计是不分前后的。3. 数据库设计与核心表结构3.1 从业务对象到数据模型拿到需求之后第一步是把业务对象翻译成数据模型。这个系统里主要实体有用户、角色、船舶、船舶证书、船员、船员证书、进出港申报单、审核记录。字典表能帮助维护港口名称、船舶类型这类枚举值。有了实体清单再去看实体之间的关系一条船对应多个船员多个证书一个申报单只对应一条船一次审核对应一个申报单。这个“一”和“多”搞清楚之后外键加在哪里就非常清楚了。我不建议为了体现水平就把表拆得特别碎。比如“证书”和“船舶基本信息”可以分离但没必再把“船主联系信息”单独拆成一张表。毕设的项目规模保持核心表在8到10张是最舒服的太多显得冗杂太少又撑不起数据分析。每张表的设计都要能回答一个问题这条数据是干什么用的谁会在哪个页面上使用它。3.2 核心表结构拆解挑两张核心表展开说。船舶信息表是这个系统的主数据表所有进出港申报都引用它字段名类型说明idbigint主键自增ship_namevarchar(50)船舶名称ship_numbervarchar(50)船舶识别号/登记号port_of_registryvarchar(50)船籍港ship_typetinyint船舶类型字典0货船1客船2渔船3旅游船tonnagedecimal(10,2)总吨位powerdecimal(10,2)主机功率(kW)lengthdecimal(10,2)船长(米)owner_namevarchar(50)船主姓名contact_phonevarchar(20)联系电话statustinyint状态0停用1正常create_timedatetime创建时间update_timedatetime更新时间进出港申报表是这个系统的核心流转表它在每次申报时生成一条记录字段名类型说明idbigint主键ship_idbigint关联船舶表declare_typetinyint1进港 2出港plan_timedatetime计划进/出港时间actual_timedatetime实际进/出港时间审核通过后写入cargo_typevarchar(50)货物类型无货物填“空载”cargo_tonnagedecimal(10,2)货物吨数passenger_countint旅客人数crew_countint随船船员数departure_portvarchar(50)出发港destination_portvarchar(50)目的港statustinyint状态0草稿1待审核2通过3驳回apply_user_idbigint申报人approve_user_idbigint审核人approve_timedatetime审核时间approve_commentvarchar(255)审核意见这两张表设计好了系统的主干就立住了。你甚至可以照着这个结构先建表再去写代码让后端代码一进入开发阶段就有明确目标。3.3 状态字段与业务流转设计为什么状态字段对这类系统特别重要因为进出港登记的所有流程本质上都是状态迁移。申报单的状态我建议用整数存0草稿、1待审核、2通过、3驳回。存整数省空间、查询快解释交给字典表页面展示时根据字典翻译成中文即可。状态迁移要控制好。草稿可以编辑、可以提交待审核的申报单不能被船方修改只能由审核员操作通过或驳回通过的申报单不能再被编辑。这些约束要在后端代码里体现不能只靠前端隐藏按钮。因为接口是可以被直接调用的如果后端不校验状态前端做了再多的按钮隐藏都是摆设。在做审核接口的时候一定要先查一遍当前申报单的状态确保它是待审核再去执行更新。我额外建议在申报表上加一个逻辑删除字段deleted用MyBatis-Plus的TableLogic管理。这样历史数据不会真删除数据库里永远能看到一艘船完整的进出港记录。评审时提到“出于数据留痕考虑不物理删除”是非常加分的细节。4. 核心功能模块实现4.1 船舶档案管理船舶档案管理就是一个典型的单表CRUD加分页查询但有几个细节让它和其他简单模块区别开来。查询条件需要支持按船舶名称模糊查询、按船籍港精确查询、按船舶类型筛选我用MyBatis-Plus的LambdaQueryWrapper解决代码非常简洁。新建船舶档案时要校验同一识别号不能重复注册这个直接用数据库唯一索引保底再在Service层提前做一次判断返回友好提示。照片和证书扫描件的上传也在这里。我的做法是在船舶信息表里存一个file_url字段保存的是文件访问相对路径文件本身写到本地上传目录然后用WebMvcConfigurer把上传目录映射成可访问的静态路径。这样上传和展示都简单不用额外引入OSS。证书到期提醒的逻辑单独写一个方法每天早上8点执行一次扫描所有证书的有效期把30天内到期或已过期的船舶找出来生成提醒记录。这个定时任务在类上标注Scheduled即可单机部署完全没问题。4.2 进出港申报与审核流程这是整个系统的核心代码逻辑要非常严谨。船方提交进港申报时后端要做的第一件事是把页面填写的字段封装成DeclareRecord对象状态设为待审核然后写入数据库。这里要注意申报单里携带的船舶ID必须当前用户有权限操作不然会出现A公司提交了一份B公司船舶的申报单这种别扭数据。我的建议是查询船舶表时加上owner_id等于当前登录用户的过滤条件。审核员处理申报单时点击“通过”按钮后端要做这样几件事校验申报单存在且状态为待审核把状态改为通过设置审核人ID和审核时间写入审核意见。如果是进港申报同时把申报计划时间写入实际进港时间。如果是出港申报写入出港时间。这四件事必须放在同一个事务里执行任何一个失败都要回滚不能出现“状态改成通过了审核人没记录”的半个成功结果。加一个Transactional注解就能搞定但很多同学经常忘导致演示数据对不上。4.3 统计报表模块统计报表是容易出彩又不难实现的部分。最常用的是两张图表按月份统计进出港数量的折线图按船舶类型统计占比的饼图。后端只需要提供两个接口一个根据declareType和月份范围算出每个月各有多少条通过状态的申报记录另一个根据ship_type分组统计每种类型船舶的申报数量。前端用ECharts一行配置就能画出图来数据对接特别快。做统计接口的时候有一个经验尽量在SQL层完成聚合不要查全表再在Java里循环累加。比如“按月统计”用SQL的DATE_FORMAT函数加GROUP BY一次查询就能返回结果如果查出来几百条数据再用代码循环分组不仅慢还要处理跨月数据这些边界情况很容易写出Bug。正确的SQL聚合逻辑会让你的代码显得专业好几个档次。5. 实操过程与关键代码解析5.1 项目初始化与基础配置我用Spring Initializr创建项目选定Java 8搭配SpringBoot 2.7.18依赖勾选Web、MySQL驱动、Redis、Validation和AOP。项目生成后再往pom里加MyBatis-Plus和JWT的依赖。这个顺序很重要先用工具生成骨架再手动补依赖能少踩很多创建项目的坑。application.yml里的配置也比较固定。数据源指向本地MySQL一定要在连接串里加上serverTimezoneAsia/Shanghai不然查询时间字段会因为时区问题前后差8个小时这就是很多同学排查半天找不到原因的“灵异事件”。MyBatis-Plus的配置记得把map-underscore-to-camel-case设为true它能把数据库的下划线字段自动映射成Java的驼峰属性减少大量手工映射。Redis的配置只管host和port密码本地开发没有就不填。分页插件也要注册一下。MyBatis-Plus默认不分页需要配置一个PaginationInnerInterceptor否则分页查询会返回全部数据。这个拦截器配置在MybatisPlusConfig类里几行代码的事但漏掉的话列表接口的分页参数就等于摆设。5.2 进出港申报的核心业务代码申报接口核心代码如下Service public class DeclareRecordServiceImpl extends ServiceImplDeclareRecordMapper, DeclareRecord implements DeclareRecordService { Override Transactional(rollbackFor Exception.class) public void submitDeclare(DeclareDTO dto) { // 校验船舶是否属于当前用户 ShipInfo ship shipService.getById(dto.getShipId()); if (ship null || !ship.getOwnerId().equals(SecurityUtils.getCurrentUserId())) { throw new BizException(不能申报不属于自己的船舶); } DeclareRecord record new DeclareRecord(); BeanUtils.copyProperties(dto, record); record.setStatus(DeclareStatus.SUBMITTED.getValue()); record.setApplyUserId(SecurityUtils.getCurrentUserId()); this.save(record); } Override Transactional(rollbackFor Exception.class) public void approveDeclare(Long id, Long approverId, boolean pass, String comment) { DeclareRecord record this.getById(id); if (record null || !DeclareStatus.SUBMITTED.getValue().equals(record.getStatus())) { throw new BizException(该申报单不在待审核状态); } if (pass) { record.setStatus(DeclareStatus.APPROVED.getValue()); record.setActualTime(LocalDateTime.now()); } else { record.setStatus(DeclareStatus.REJECTED.getValue()); } record.setApproveUserId(approverId); record.setApproveTime(LocalDateTime.now()); record.setApproveComment(comment); this.updateById(record); } }这段代码有两点值得学习。第一所有涉及写操作的Service方法都加了Transactional要么全成功要么全回滚。第二审核方法的第一行就是状态校验待审核状态才能被审核。如果跳过了这一步脏状态数据就会悄悄溜进数据库后面追查要花好几倍时间。我会在Service层做业务校验Controller只负责参数接收和返回结果不写业务逻辑。这个分层习惯答辩时也能讲得很清楚。5.3 并发与接口安全设计进出港申报系统有一个很实际的并发场景同一艘船同时被提交进港和出港申报。在真实业务里一艘船不可能既在进港又在出港系统必须拦截这种情况。我的做法是业务层加检查逻辑在提交申报前查询该船是否存在状态为“待审核”或“通过”且没有实际完成对应操作的申报单如果存在就提示“该船尚有未完成的进出港申报”。这个检查在单机部署、毕设演示场景下已经够用了。想再加强一点可以在数据库层面给ship_id加一个唯一约束比如建一个“当前在港状态”字段出港申报通过时将状态改成离港下一次进港才允许。不过加字段会提高复杂度如果没有十足把握还是以业务层校验为主。答辩时如果能讲出“我对同一船舶重复申报做了限制避免数据矛盾”老师会觉得你想问题很周全。接口安全方面除了JWT拦截器我再提醒两个容易忽略的点。第一文件上传接口必须限制文件类型和大小application.yml里设置max-file-size为10MBService层校验图片扩展名不放行可执行文件。第二管理端删除接口一定要做权限校验普通船方用户不能调删除接口否则他把自己提交过的申报单删掉审核数据就凭空消失了。这部分通过自定义注解RequireRole(ADMIN)加拦截器实现代码量不大但很安全。6. 常见问题与毕设避坑指南6.1 开发阶段的典型疑难杂症汇总一下大概率会撞上的坑每个都是我亲眼见过同学踩进去的。第一个坑是时区错乱。现象是数据库存的时间和页面显示的时间差了8小时解决方法是连接串加serverTimezoneAsia/ShanghaiJVM启动参数再带上-Duser.timezoneAsia/Shanghai双保险。第二个坑是列表页分页不生效。八成是忘了注册PaginationInnerInterceptor加上之后就好了。第三个坑是前端访问上传图片404。这是因为没有把本地上传目录映射成静态资源路径需要在WebMvcConfigurer里addResourceHandlers手动映射。第四个坑跨域。前端在8080端口后端在8081端口浏览器会拦截跨域请求。写一个CorsConfig放行前端地址即可方法名不用复杂关键是allowedOrigins要写对。第五个坑是MyBatis-Plus字段下划线映射问题。数据库字段是create_time实体属性是createTime如果不开启驼峰映射查出来的createTime永远是null。我的建议是从一开始就把配置写好不要等出了问题再补。还有一个容易忽略的是逻辑删除冲突。用TableLogic之后MyBatis-Plus的查询语句会自动拼接deleted0但如果你在SQL里用了自定义的JOIN查询MyBatis-Plus不会自动给你加这个条件。最稳妥的做法是核心查询都走MyBatis-Plus内置方法简单又安全。实在要写JOIN就在SQL里手动加and deleted0避免查询出已经被逻辑删除的数据。6.2 论文结构和答辩准备论文标准结构建议是绪论、相关技术介绍、需求分析、系统设计、系统实现、系统测试、总结。有同学会问要不要单独写一章“难点分析”我的建议是把难点分散到设计和实现章节里去讲比如并发控制放在系统设计事务处理放在系统实现比单独罗列更自然。画图是论文的门面。用例图、E-R图、系统架构图、业务流程图、时序图这五张图必不可少。E-R图可以直接根据第3节的表结构生成注意把实体名、属性名、一对多关系画清晰。用例图重点体现三类角色各自的操作权限评审老师翻论文一般先看图再看字图的质量直接影响第一印象。不要用在线工具生成的默认样式图里中文乱码看起来会很不专业。答辩惯常会问的问题我也整理一下。第一“为什么选择SpringBoot”答简化配置、内嵌服务器、生态完善项目快速成型。第二“系统解决了什么实际问题”答取代纸质登记实现数据实时流转和自动统计同时通过证书到期提醒降低超期作业风险。第三“同一艘船重复申报怎么处理”按上面的业务校验逻辑回答。第四“你的权限是怎么实现的”从JWT加拦截器讲起。第五“数据库为什么这么设计”从业务实体关系展开。这些问题你在开发过程中都真实处理过回答起来就不会心虚。最后再分享一个实用技巧正式演示前一定要准备一套“干净又有代表性”的演示数据。船舶类型覆盖货船、客船、旅游船各一条申报记录覆盖通过、驳回、待审核三种状态最近三个月至少有一个明显的进出港趋势。演示时先把“提交申报、审核通过”走一遍再点开统计页面展示图表变化整个过程不超过五分钟但把你系统的业务完整性和技术实现都展示出来了。这个知识点很多同学是到答辩前两天才意识到那时候再录数据就手忙脚乱了。我每次帮同学把关项目一定会提醒这一点你代码写得再好数据不整评委是看不到你的亮点的。
返回列表