ARTICLE DETAIL

资讯详情

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

Spring Boot停车场管理系统毕设全解析:从数据库设计到并发控制

Spring Boot停车场管理系统毕设全解析:从数据库设计到并发控制 又到毕设季“停车场管理系统”绝对是 Java 方向出镜率最高的题目之一。前两天有个学弟拿着一套号称“完整前后端代码 说明文档 LW”的成品来找我说代码能跑但答辩时老师一句“这个车位状态是怎么保证不超卖的”直接把他问懵了。我听完特别有感触因为我自己也做过基于 Spring Boot 的校园停车场管理系统正是以高校校园停车场景为蓝本设计的前后端分离、带完整文档后来还把它写进了简历。这篇博文就把整个项目的完整复盘分享出来包括需求分析、技术选型、数据库设计、接口实现、前后端联调部署以及论文和答辩准备。如果你是正在做毕设的 Java 方向学生或者想拿一个能说清楚的项目去面试这篇文章应该能帮你少走不少弯路。1. 停车场管理系统凭什么成为毕设“常青树”校园场景又有什么不同1.1 毕设选它的逻辑业务闭环完整技术栈覆盖面广停车场管理系统之所以常年出现在毕设选题列表里核心原因不是它简单而是它恰好构成一个完整的业务闭环用户注册登录、车位状态查询、预约、入场、计费、支付、订单管理、报表统计。这个闭环里既有纯数据操作又有状态流转和并发问题前后端交互也足够丰富能同时覆盖 Spring Boot、MySQL、Vue 等主流技术栈对于本科毕设来说难度适中又不会让人觉得太水。更重要的是这类系统在演示环节非常直观。评委老师说“你现场演示一下”你可以打开页面注册一个账号选一个车位模拟入场计算费用再把订单查出来整个过程每一步都有页面反馈比那些纯算法或纯后台接口项目好讲得多。1.2 校园停车场景和商业停车场完全是两码事如果题目只是“停车场管理系统”很多学生会照抄网上的通用模板临时车按小时计费月租车按月计费管理员增删改查。但一旦题目带上了学校背景比如“莆田学院停车场管理系统”需求就必须重新梳理。高校停车场的典型特点是固定用户多教职工和学生的车辆长期停在同一片区上下课和考试周会出现明显的潮汐式拥堵访客车辆虽然少但通常集中在招生季、家长会等特定时间段。车位紧张时需要区分教工车位、学生车位、访客车位甚至要支持室内室外分区编号。如果忽略这些场景差异拿一个商业停车场模板直接改个校名答辩时老师只要问一句“你的系统怎么处理高峰期车位优先分配”就很难接住。所以第一步别急着写代码先把你所在学校或目标学校的真实停车规则整理成文字再转成用例后面所有设计都会顺利很多。1.3 底线要求哪些功能必须有结合我做的校园版停车场系统功能清单大致如下功能模块功能点角色说明用户管理注册、登录、个人信息维护用户/管理员密码加密存储支持管理员重置车位管理车位新增、编辑、删除、状态查看管理员车位编号、区域、类型、状态预约管理预约车位、取消预约用户限定未来 N 小时内的预约防止恶意占位停车管理入场登记、出场结算用户/管理员支持手动输入车牌号模拟识别计费管理按时计费、包月计费系统临时车按小时或按次包月车自动识别订单管理订单查询、导出管理员按时间、车牌号、状态筛选数据统计车位使用率、收入统计管理员图表展示这张表建议边画边想接口后面设计数据库时能直接映射。不要先把表建好再回头看功能那样功能容易漏。2. 角色与核心流程拆解把“停车”变成状态机2.1 三种角色就够了别把权限做复杂我先按“管理员、普通用户、临时访客”三种角色来设计。管理员负责维护车位和用户数据查看订单与统计普通用户是已经注册的校内教职工或学生可以预约车位采用包月或临时停车的方式临时访客则不需要注册入场时输入车牌号即可出场时按临时计费标准结算。很多人会把临时访客也设计成必须注册这其实是给自己找麻烦。访客进入系统的核心诉求是“交钱走人”你让他先填一堆注册信息系统体验很别扭答辩演示也啰嗦。为了简化访客模块只需要车牌号和入场时间出场再绑一次手机号都可以不做。这个取舍在毕设阶段是完全合理的。2.2 入场、出场、预约三大流程停车业务的灵魂是状态流转我习惯用一张状态变换表来约束自己状态含义触发动作说明FREE空闲管理员新增车位、用户出场释放可被预约或直接入场RESERVED已预约用户发起预约车位被锁定但车辆未到场OCCUPIED占用入场登记确认车辆已停在车位上MAINTENANCE维护中管理员标记不参与分配入场流程用户开进场口摄像头识别车牌演示时可手动输入→ 系统查找空闲车位 → 分配车位普通用户可预约或直接入场→ 生成停车记录状态改成 OCCUPIED。出场流程识别车牌 → 获取入场时间 → 计算费用 → 生成待支付订单 → 用户支付演示可模拟→ 车位状态改回 FREE订单完成。预约流程用户选择空闲车位和预约时间段 → 系统锁定车位写预约表车位状态改 RESERVED→ 用户在规定时间内入场完成停车否则预约自动过期车位释放回 FREE。这三个流程里最容易出问题的是预约超时和车位并发分配。超时可以用定时任务扫描把超时未确认的预约状态改成取消并释放车位并发分配则需要靠数据库锁或版本号控制我会在数据库设计章节详细讲。2.3 为什么先画流程图再写代码我见过不少同学一拿到题目就打开 IDEA 建实体类然后陷入“这个字段补一下、那个接口补一下”的泥潭。说句难听的这就是需求没想清楚。哪怕是我个人很熟练了也还是先在纸上把三类角色的操作路径走一遍把每个页面、每个接口、每次状态变化的触发源标出来再开始编码。代码是需求表达需求没定代码怎么写都是缝补。具体做法很简单用 Word 或 Excel 的图形画一张泳道图横轴是用户、管理员、系统纵轴是时间线把入场、出场、预约、支付、报表这些场景都走一遍。不用画得很专业但必须把每一步的输入输出写清楚。这张图做好了后面写论文的“需求分析”和“总体设计”章节也能直接复用一举两得。3. 技术选型与项目结构Spring Boot Vue但版本选择很关键3.1 技术栈清单与选择理由我的建议是后端 Spring Boot MyBatis-Plus MySQL前端 Vue Element UI认证用 JWT数据访问层用 MyBatis-Plus 而非原生 MyBatis。理由很单纯生态成熟、资料多、代码量少毕设时间宝贵没必要在 JDBC 或 XML 映射上耗费精力。有的同学会纠结要不要上 Spring Cloud、Redis、RabbitMQ。我的建议是毕设系统本质上是一个单体应用刻意引入微服务只会增加部署和讲解难度比如分布式事务怎么做、服务怎么拆分这些问题很可能把你自己绕进去。Redis 可以作为亮点加但别依赖它做主流程否则评委会追问数据一致性问题。层次选型版本建议说明后端框架Spring Boot2.7.x兼容 JDK8教程最多稳定ORMMyBatis-Plus3.5.x不用手写大量 SQL自带分页和乐观锁插件数据库MySQL8.0支持 JSON时区问题需注意认证JWT Spring Security可选jjwt 0.11.x前后端分离无状态认证前端框架Vue2.x Element UI 或 3.x Element Plus后台管理场景Vue2 更顺手HTTP 客户端Axios1.x统一请求拦截处理构建工具Maven / npm3.8打包部署部署Nginx / Tomcat-前后端分离部署方式3.2 Spring Boot 版本别盲目追求新关于版本我特别想多说一句。现在最新版 Spring Boot 3.x 已经普及但很多毕业设计环境还是 JDK8 老教程用 2.7.x 其实更稳妥。原因有三第一2.7 还支持 javax 命名空间网上绝大多数资料可以直接照用第二Spring Security 5 和 MyBatis-Plus 的适配问题少第三Tomcat 环境下部署 war 包和各种插件兼容性更好。除非你就想刻意用 JDK17 Spring Boot3 来体现“新”否则不要给自己挖坑。当然如果你已经熟悉 3.x 的 jakarta 命名空间调整、Spring Security 6 配置写法变化用它也没问题。但注意这类差异在答辩时很容易被问到你得能解释清楚。3.3 项目目录与分包习惯后端按技术层次分包是毕设最常见的做法项目规模不大时足够清晰。我的习惯是在 controller、service、mapper 里面再按业务模块建子包这样既不会让目录太碎也不会随着业务增加而失控。src/main/java/com/example/parking/ ├── common/ // 统一 Result、异常处理、常量 ├── config/ // 跨域配置、MyBatis-Plus 配置、JWT 拦截器 ├── controller/ // 接口层下面分 admin / client ├── service/ // 业务逻辑层下面分 user / parking / order ├── mapper/ // 数据访问层 ├── entity/ // 数据库实体 ├── dto/ // 入参对象 └── vo/ // 返回前端对象前端项目用 Vue CLI 或 Vite 初始化核心结构放在src/api、src/router、src/store、src/views四个目录。src/api里按模块封装接口调用不在组件里直接写 axios方便维护。3.4 关于若依框架和“零基础脚手架”的看法搜索框里经常有人问“若依框架前后端分离”若依确实是一个很成熟的前后端分离脚手架自带权限、代码生成、用户管理等能极大缩短开发时间。我想说的是如果你只是想把毕设快速做出来用若依没问题但答辩前至少要能讲清楚它的权限模型RBAC和代码生成逻辑。不然老师问“你怎么做权限的”你说“若依自带”分数不会高。我更推荐的做法是对照若依的启动流程自己手搭一个精简版 Spring Boot Vue 项目。不需要完全从零写但核心的 JWT 登录、路由拦截、角色权限最好自己敲一遍。这个过程既让你理解整个请求链路还能顺便把“前后端分离项目实战”这个项目经历写进简历比直接引用脚手架有说服力得多。4. 数据库设计千万别把车位和订单放进同一张表4.1 核心表结构与设计理念数据库是停车场系统的地基。我最初犯过一个错误试图把车位信息、停车订单、预约信息放在一张大表里字段越加越多最后各种状态互相覆盖。后来把表拆开思路立刻清晰。核心表大致五张。用户表parking_userid、username、password、phone、role、plate_no、status、create_time。其中 password 必须存加密值推荐 BCrypt不要再存明文。plate_no 允许为空因为访客不一定注册账号。车位表parking_spaceid、space_code、area、location、type、status、lock_version。type 区分普通/新能源/无障碍status 用 0 空闲、1 占用、2 预约、3 维护四个值。lock_version 很重要是后续做并发控制用的乐观锁字段。CREATE TABLE parking_space ( id BIGINT NOT NULL AUTO_INCREMENT, space_code VARCHAR(20) NOT NULL COMMENT 车位编号如 A-01, area VARCHAR(50) DEFAULT COMMENT 区域, type TINYINT DEFAULT 0 COMMENT 0普通 1新能源 2无障碍, status TINYINT DEFAULT 0 COMMENT 0空闲 1占用 2预约 3维护, lock_version INT DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY uk_space_code (space_code) );停车记录表parking_recordid、user_id可空、plate_no、space_id、entry_time、exit_time、fee、status、create_time。user_id 允许空是因为访客可能没有账号必须单独存 plate_no。预约表parking_reservationid、user_id、space_id、reserve_start、reserve_end、status、create_time。status 区分待入场、已完成、已取消、已过期。计费规则表charging_ruleid、rule_name、type临时/包月、price、unit小时/月、status。有这张表的好处是计费标准可以后台配置而不是写死在代码里。4.2 状态字段与索引看似小事实际是答辩加分点状态字段建议统一用 int 加注释而不要用字符串因为字符串容易散比如有人写“空闲”有人写“0”还有人写“FREE”乱成一锅粥。交给 int 配合枚举类前后端都用数字业务层再做映射。索引这部分建议在 plate_no、space_id、entry_time、user_id 上都建普通索引。真实场景中查订单最先按车牌查、按时间范围查没有索引的数据表在数据量稍大时就会慢得明显。答辩时能说出“我在出场结算接口的 plate_no 上建了索引因为高频查询按车牌定位当前停车记录”这比空谈性能优化有用得多。4.3 并发抢车位用乐观锁解决超卖问题这是整个项目里最有含金量的一块也是评委最喜欢追问的点。想象一下两个用户同时点击同一个空闲车位如果接口只是先查再改两个请求都会读到 status0然后都更新成占用就会造成一个车位被两个人预约。这其实就是电商“超卖”问题。解法有两种。权限不大时用乐观锁即可。在车位表里加一个 version 字段更新时带 where 条件status 0 AND version #{oldVersion}更新成功再把 version 加一。MyBatis-Plus 开启乐观锁插件后只要在实体类 version 字段上标注Version框架自动完成校验。如果用了 Redis还可以用分布式锁或原子操作车位剩余数量用 Redis 的decrement减到负数即失败。这个方案听起来更高级但毕设阶段我更推荐乐观锁因为逻辑直观也方便用代码演示给老师看。4.4 数据库时区与跨天计费的两个真实坑第一个坑是 LocalDateTime 和 MySQL 时区不一致。Spring Boot 里如果直接用Date前端很容易出现少 8 小时的问题。建议实体字段统一用LocalDateTime数据库类型用datetime并在 JDBC 连接串上显式配置serverTimezoneAsia/Shanghai避免部署到云服务器后时间错乱。第二个坑是跨天计费。如果入场时间是昨晚 22 点出场是凌晨 1 点3 小时是不是都按白天的时段单价算校园停车场一般会区分白天和夜间收费标准这个逻辑如果写在接口里会很乱。我的做法是把计费规则拆成时间段表包含 start_time、end_time、price结算时按入场时间到出场时间逐段取交集计算。毕设如果不想做这么细至少也要在charging_rule里区分按次和按时同时写清楚跨天按自然小时累计而不是直接拿日期相减。5. 后端实现细节统一返回、JWT、和那个最容易写乱的结算接口5.1 统一返回体和全局异常处理前后端分离项目必须约定一个统一返回结构我的是ResultT包含 code、msg、data 三个字段。code200 成功code401 未登录code500 业务错误。所有接口都返回这个对象前端拦截器看到 code 统一判断不用每个页面单独处理错误。全局异常处理用RestControllerAdvice把参数校验异常、业务异常、未知异常统一交给一个类处理。这样就算代码里漏了 try-catch前端收到的也是结构一致的错误信息不会直接抛出丑陋的堆栈。这个习惯同样可以用在你以后的工作项目中。5.2 JWT 登录认证流程登录流程很简单用户提交账号密码后端校验 BCrypt 加密密码通过后生成 JWT 返回前端。前端把 token 放在 localStorage之后所有请求在 axios 拦截器里加上Authorization: Bearer xxx。后端再写一个 HandlerInterceptor放行登录、注册、车位查询等公开接口其余接口必须解析 token 拿到当前用户信息。为什么用 JWT 而不用 Session核心原因是前后端分离后后端不再维护 Session部署多个实例时也不需要处理 Session 同步。虽然 JWT 有无法主动注销的问题但毕设场景下完全可控。答辩如果问“JWT 安全性”你可以回答token 有效期内确实无法手动失效所以我把有效期设置得比较短并提供刷新接口这算是标准做法。5.3 核心接口清单与流程说明接口列表如下方法路径功能角色POST/api/auth/login登录公开GET/api/space/list车位列表按状态过滤已登录/公开POST/api/space/add新增车位管理员POST/api/reservation/create预约车位用户POST/api/reservation/cancel取消预约用户POST/api/parking/entry入场登记用户/管理员POST/api/parking/exit出场结算用户/管理员POST/api/payment/pay模拟支付用户GET/api/order/page分页查询订单管理员GET/api/stats/overview今日车流量、收入统计管理员这 10 个接口基本覆盖核心业务做的时候可以先用 Postman 跑通再对接前端。5.4 出场结算接口的代码设计出场结算是业务中心我建议单独抽一个ParkingChargeService不要塞在 Controller 里。核心逻辑是根据车牌找到未出场记录 → 设置出场时间 → 根据是否包月、是否预约判断计费规则 → 计算费用 → 生成订单 → 更新车位状态和记录。如果暂时不做跨时间段计费最简单的方式是用duration exitTime - entryTime乘以每小时单价后向上取整。注意一定要向上取整因为停车场普遍按“不足一小时按一小时”收费。示例long minutes Duration.between(entryTime, exitTime).toMinutes(); long chargeHours (minutes 59) / 60; // 向上取整 BigDecimal total hourlyPrice.multiply(BigDecimal.valueOf(chargeHours));这个向上取整的微小细节很多人都会忽略。演示时如果出现计费结果和预期差 1 元往往会复盘半天其实问题就在这里。5.5 日志和慢 SQL 排查后端联调时最头疼的是“接口报错但控制台没输出”。我后来习惯在全局异常处理器里打印完整堆栈并在 service 层关键方法打业务日志比如“用户 xx 预约车位 xx 成功”。配合 MyBatis-Plus 的日志配置把 SQL 语句和执行结果输出到控制台接口出问题基本一眼就能定位。项目上线前再把日志级别调成 info避免刷屏。6. 前端与联调从跨域到部署一次讲清楚6.1 Vue 项目骨架和页面结构前端部分用 Vue 2 Element UI 搭后台管理界面登录页、主页框架、车位管理页、停车记录页、订单页、统计页。如果采用 Vue 3就装 Element Plus代码风格类似。核心思路是先把接口封装好再做页面页面只关心数据渲染前后端联调会顺畅很多。6.2 axios 封装拦截器里统一处理 token 和 401在src/utils/request.js里创建 axios 实例设置baseURL在请求拦截器加上 token在响应拦截器判断code。比如 code 是 401 就清空本地登录信息并跳转登录页code 是 500 就用 Element UI 的 Message 组件弹出错误提示。这样每个页面只需要关心成功的数据错误处理完全无需重复写。6.3 联调跨域开发环境用 proxy生产环境用 Nginx本地开发时前端跑在 8080后端跑在 9090直接请求会有跨域问题。最简单的做法是在vue.config.js里配置 proxymodule.exports { devServer: { proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }这样前端代码里的请求地址写/api/xxx开发时由 webpack 代理转发到后端不需要后端额外开启 CORS。如果后端一定要开 CORS 也行在 config 里加一个跨域过滤器不过我更推荐用 proxy因为生产环境 Nginx 反向代理和它是一模一样的思路。6.4 车位可视化面板轮询还是 WebSocket在首页展示停车场总览图是很多同学觉得最“有面子”的功能。实现不复杂先用表格或卡片把车位位置画出来每个车位根据状态显示不同颜色灰色空闲、蓝色预约、红色占用。实时性要求不高时用setInterval每隔 5 秒调一次车位列表接口刷新即可工作量小稳定。想进阶可以用 WebSocket 在后端主动推送状态变化。Spring Boot 配置 WebSocket 端点用户入场或出场时服务端广播消息前端监听后再局部更新。这个方案演示效果更好也会增加复杂度。毕设想拿高分可以加想尽快交差就选轮询。6.5 打包部署jar Nginx 还是 war Tomcat这里把“tomcat部署前后端分离项目”讲清楚。前后端分离项目最常见的部署方式是前端npm run build生成dist静态目录交给 Nginx 托管后端打成 jar 包用java -jar启动Nginx 里将/api路径反向代理到后端端口。这个方案干净也贴近企业生产环境。如果毕设老师要求必须用 Tomcat 部署也可以。后端pom.xml里把打包方式改成war排除 Spring Boot 内置 Tomcat打出来的 war 包放进 Tomcat 的webapps目录前端dist文件夹也放到 Tomcat 的webapps/ROOT下并配置后端接口地址为相对路径。要注意接入 context path 时axios 的baseURL不能写死http://localhost:9090最好用相对路径/api否则容易在 Tomcat 部署时找错接口。7. 说明文档与答辩把项目“讲成自己的”7.1 论文如何从零开始写而不是 CtrlC毕设中常说的 LW也就是说明文档/论文。我自己的经验是千万不要先写完代码再硬套论文模板而是边开发边沉淀文档。每做完一个模块立刻把它的需求背景、设计思路、核心实现、遇到的问题和解决办法记下来最后组装成论文时全是第一手素材根本不用编。论文一般包含摘要、绪论、需求分析、总体设计、详细设计、系统实现、系统测试、总结展望。其中需求分析和总体设计是最能体现“你自己做过”的部分。前面画的流程泳道图、状态图直接放进需求分析数据库表结构和接口设计放到详细设计测试章节可以列功能测试用例表黑盒用例加边界值用例比如“同一车位并发预约”这个用例就很有含金量。7.2 答辩高频问题清单与回答方向我整理了一批这类项目必被问到的问题建议每个都准备两三句话问题回答思路为什么用 Spring Boot 而不用 SSH/SSM自动配置、内嵌容器、生态丰富开发效率高为什么用前后端分离前后端并行开发、独立部署职责清晰JWT 和 Session 有什么区别无状态 vs 有状态跨域友好但注意失效问题并发预约同一个车位怎么办乐观锁版本号或数据库行锁演示更新语句的 where 条件数据库怎么保证数据一致性事务 锁扣款和更新订单必须在一个事务里为什么给 parking_record 建索引高频按车牌和时间查订单避免全表扫描你做了哪些测试功能用例、接口用 Postman 测、少量并发场景模拟这些问题就是我前面章节写过的内容。你能把答案用自己的话讲通老师就会相信这套代码是你写的。7.3 演示时最稳妥的顺序答辩演示别一上来就点统计图表先走主流程。我的推荐顺序管理员登录 → 新增一个车位 → 新建一个测试用户 → 用户登录 → 预约这个车位 → 入场确认 → 模拟出场 → 自动计算费用 → 支付 → 订单列表看到记录 → 回到首页看到车位释放。这一套走下来核心功能全部覆盖也不会因为某个图表加载慢而冷场。再准备一条“分支路径”比如用户取消预约、管理员禁用某个车位、非法 token 访问接口被 401 拦截。分支路径不是必须演示但如果评委时间多你主动展示边界处理会很加分。7.4 关于成品代码与调试定制的善意提醒市面上的“完整前后端代码 说明文档 LW”定制服务不少我的态度是可以买来参考但别把它当最终交付物。首先很多成品代码存在结构混乱、字段名随意、时间处理违规的问题其次毕设答辩时老师只认你这个人你真的对项目一知半解几句话就露馅。买了代码之后一定要自己动手跑通、读一遍核心模块把包名和数据库信息改成自己学校场景的地址和规则比如把区域改成以自己学校命名的停车场。这个“调试定制”的过程本质就是学习过程也是你从学生转向开发者最实在的一段经验。最后再分享一个我自己的体会做完这个系统后我没有止步于交稿而是把项目里“并发车位分配”和“跨时间计费”两个点写了技术笔记后来实习面试时面试官正好对停车管理系统的并发处理感兴趣我把乐观锁和事务方案讲清楚当场就看到对方点头。所以别把这个题目当成普通作业把它当成面试时的项目谈资好好打磨回报不止一个学位。
返回列表