ARTICLE DETAIL

资讯详情

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

Spring Boot+MyBatis公共自行车租赁系统:从数据库设计到部署实战

Spring Boot+MyBatis公共自行车租赁系统:从数据库设计到部署实战 1. 项目概述与核心需求拆解1.1 这到底是个什么系统每年到毕设季我都会收到不少类似的问题老师给了一个某某管理系统的题目感觉很简单真做起来又不知道从哪下手。泗洪县公共自行车在线租赁管理系统就是这类题目的典型代表——表面上是个管理系统实际上里面藏着租赁流程、计费逻辑、车辆状态流转这些真正的业务难点。这个项目解决的问题很清晰泗洪县投放了一批公共自行车分布在县城各个站点市民取车、骑行、还车都需要一个线上平台来支撑。系统要管住三类东西车辆在哪、车辆什么状态、谁在用什么车。再往后延伸就是费用怎么算、记录怎么查、管理员怎么维护数据。把这几个问题想透了项目骨架就出来了。这套系统适合谁参考如果你是计算机相关专业的应届生拿这个题目做毕设读这篇能帮你避开大部分坑如果你是刚入行的后端开发想找一个练手项目这套系统的业务复杂度也刚好合适——比纯CRUD有深度又不像大厂微服务那样一上来就把人劝退。1.2 核心角色与功能边界很多同学拿到题目就急着建项目、写代码结果写到一半发现咦这个功能好像没地方放。我建议先做一件事把角色和功能边界写在纸上。这个系统按业务来分至少有四类核心功能用户端普通市民注册登录、个人信息维护、余额充值、扫码借车、站点还车、租车记录查询。管理端系统管理员自行车管理新增、编辑、报废、报修、站点管理、用户管理冻结/解封、租还订单查询与异常处理。运营端可选加分项车辆调度管理把车辆从满站点调度到空站点并生成调度记录。统计报表租车量趋势、热门站点排行、营收统计、车辆使用率。我特别建议你在论文里加运营调度这个角色。为什么因为绝大多数同学做的公共自行车系统只有用户管理员两个角色你要是把调度员这块做出来业务闭环就完整了——有人借车造成站点车辆变少调度员补车车辆状态恢复正常。这在答辩时是一个很能讲的业务亮点。功能边界理清楚后你会发现这项目本质上就四件事用户管理体系、车辆状态机、租还订单流水、后台数据统计。后面的所有代码都是围绕这四件事展开的。2. 技术选型与整体架构2.1 为什么选 Spring Boot 而不是别的题目已经限定用 Spring Boot 了但我想说说为什么这个选择本身是合理的。Spring Boot 最大的价值不是新而是省事。它通过自动装配把 Spring 时代的 XML 配置大量收敛掉内嵌 Tomcat打一个 jar 包就能跑。对于毕设这种开发周期短、单人完成的项目来说这个特性太重要了——你不需要花两周时间去折腾环境可以把精力全部放在业务代码上。再说自动装配原理。很多同学在简历上写熟悉 Spring Boot一问自动装配就答不上来。其实核心就一句话SpringBootApplication里的EnableAutoConfiguration会去读所有 jar 包里的META-INF/spring.factoriesSpring Boot 3.x 是AutoConfiguration.imports按条件注解ConditionalOnClass、ConditionalOnProperty决定哪些配置类生效。你引入spring-boot-starter-web后DispatcherServlet、Tomcat、Jackson这些配置类发现类路径里有对应的类就自动加载。理解这一点答辩的时候被问到Spring Boot 为什么能自动配置就不会卡壳。2.2 配套技术栈和版本选择这个项目的标准配法是Spring Boot MyBatis MySQL Vue Element UI。前端用 Vue 做页面后端提供 RESTful 接口。版本选择这里必须多说一句因为Spring Boot 版本太高真的是我见过最多的坑。很多同学图新鲜直接上 Spring Boot 3.x然后发现3.x 强制要求 JDK 17 及以上如果你的机器还是 JDK 8直接跑不起来。3.x 把javax.*换成了jakarta.*网上大量的老教程代码直接报错。3.x 里 MyBatis 的官方集成包从mybatis-spring-boot-starter换成了mybatis-spring-boot-starter适配版本一不小心版本对不上就启动失败。我的建议是毕设求稳直接用 Spring Boot 2.7.x JDK 8 MyBatis 1.3.x。别觉得版本旧就是技术落后把自己的核心业务做扎实比追新版本有用得多。如果你确实想上 3.x也行但一定确认 JDK 版本是三件套里最先检查的。2.3 项目目录结构与分层设计我见过太多毕设代码长这样所有 Controller 塞在一个包SQL 全部写在 Service 里实体类一个 Lombok 注解都没有。这种代码答辩时老师翻两页就不想看了。建议按这样的分层来组织com.sihong.bike ├── controller // 接口层只做参数接收和结果返回 ├── service // 业务层写核心逻辑 │ └── impl // Service 实现 ├── mapper // MyBatis 数据访问接口 ├── entity // 数据库实体 ├── dto // 传输对象比如登录请求、租车请求 ├── vo // 视图对象返回给前端的组装数据 ├── config // 配置类如 MyBatis、CORS、拦截器 ├── common // 公共类统一返回结果、异常处理、工具类 └── BikeApplication // 启动类Controller 里别写业务逻辑Service 里别直接操作 HttpSession 和前端参数Mapper 接口和 XML 一一对应。分层清楚之后你会发现写代码的速度反而更快——因为每段代码该放哪、该干什么你根本不用想。3. 数据库设计与核心表结构3.1 实体关系梳理数据库设计是这类系统最见功力的一环。我见过很多同学的表设计最大的问题是状态全靠感觉——车辆有没有被借出居然靠订单表里有没有未还记录来判断这其实很危险。核心实体关系其实不复杂用户表、站点表、车辆表、订单表、充值记录表再加上一张调度记录表如果做运营端的话。关系主要是三条一个站点有多辆车车辆归属于站点。一个用户有多条租借订单。一条订单关联一个用户、一辆车、一个取车站点和一个还车站点。这里有一个关键的建模决策车辆和站点是不是强归属关系我的做法是车辆表里存一个current_station_id表示当前所在站。这样还车时更新这个字段统计车辆在哪个站点就非常简单。要是不做这个字段每次查某站现在有多少车都得去订单表里算性能差还容易错。3.2 核心表设计与关键字段说明下面这几张表是这个系统的命脉字段设计值得你多花时间用户表t_userCREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, phone VARCHAR(20), balance DECIMAL(10, 2) DEFAULT 0.00, status TINYINT DEFAULT 1 COMMENT 1正常 0冻结, create_time DATETIME );注意几点密码字段长度留到 100因为 BCrypt 加密后的字符串有 60 位余额用DECIMAL(10,2)绝对不要用FLOAT否则金额会出现 0.10.2 不等于 0.3 的经典精度问题状态字段用TINYINT比用字符串好理由很简单——省空间而且状态机流转时用数字判断更干净。车辆表t_bikeCREATE TABLE t_bike ( id BIGINT PRIMARY KEY AUTO_INCREMENT, bike_no VARCHAR(30) NOT NULL UNIQUE, station_id BIGINT, status TINYINT DEFAULT 1 COMMENT 1可租 2已借出 3维修 4报废, create_time DATETIME );车辆状态只有四个但这四个状态之间的流转规则是业务核心可租的车才能被借出借出后变成已借出还车时必须把状态改回可租同时更新station_id被报修的车不能参与租借报废车只能由管理员处理。这个状态机一定要在 Service 层写清楚不能靠前端控制。订单表t_rental_recordCREATE TABLE t_rental_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, bike_id BIGINT NOT NULL, rent_station_id BIGINT, return_station_id BIGINT, rent_time DATETIME, return_time DATETIME, duration_minutes INT, amount DECIMAL(10, 2), status TINYINT COMMENT 1租赁中 2已还车 3异常, create_time DATETIME );这张表是计费的核心。免费时长、超时费率这些参数不要写死在 SQL 里建议在系统配置表里存一份比如free_minutes30、hourly_fee1.00这样后期调价不用改代码答辩时也能说系统支持动态计费配置。3.3 索引和事务的设计思路索引方面别贪多抓两条主线就行订单表上user_id加索引因为用户查自己的租车记录是最频繁的查询bike_id和status组合索引也可以加一个但如果你只在查询车辆状态时用单列索引其实就够。事务方面重点是租车和还车这两个操作。租车不是简单的 insert 一条订单就能完事它涉及到检查车辆状态、检查用户状态、生成订单、更新车辆状态。这四个步骤谁都不能少其中任何一步失败前面成功的操作都得回滚。这就是为什么我建议在 Service 层方法上用Transactional而且只在真正需要事务的地方用——不是所有查询都加加了反而拖慢性能。4. 核心功能模块与实操实现4.1 注册登录与 JWT 鉴权用户模块是每个系统都有的但很多同学做得太裸——前端跳个登录页后端校验下用户名密码就完事了。还是那句话毕设要体现出工程思维鉴权这块建议用 JWT。实现思路不复杂用户登录成功后后端用密钥生成一个带过期时间的 Token 返回给前端前端存在本地每次请求在请求头里带上Authorization: Bearer xxx后端写一个拦截器统一解析 Token校验通过就放行并把用户信息塞到请求上下文里。这里有个容易踩的坑Token 密钥不要写死在 Controller 里放配置文件的jwt.secret字段里后面改起来方便。另外密码一定要加密存储用 Spring Security 里的BCryptPasswordEncoder就行不要自己写 MD5。答辩时老师大概率会问密码是怎么保存的你答BCrypt 加盐哈希每次加密结果不同即使数据库泄露也无法逆向这就是标准答案。4.2 公共自行车的完整租还业务流程租车业务流程是这套系统里最有说头的地方。前端扫码或者选车后调用POST /api/rental/rent接口后端要做的事按顺序是从 Token 里解析出当前用户校验用户状态是否正常。查车辆信息确认车辆状态是可租。查用户余额是否为负数或者是否有未还车辆防止有人同时借两辆。生成一条订单状态设为租赁中记录取车站点。更新车辆状态为已借出清空当前站点编号。返回订单号给前端。这里有一个细节我建议你注意第 3 步查是否有未还车辆其实更严谨的做法是在订单表上给user_id和status加一个联合约束或者用一个 Redis 锁来防止并发下用户同时提交两次租车请求。毕设如果不想引 Redis最简单的办法就是在 Service 方法上加synchronized同步虽然性能不咋样但逻辑上是安全的。答辩时能把这个并发问题讲出来是非常加分的。还车业务流程比租车还要多几个步骤校验订单存在且状态为租赁中。校验还车站点存在且站点当前车辆数小于容量上限。更新订单填还车时间、还车站点计算骑行时长。按计费规则计算金额免费时长内不收费超出部分按小时计费不满一小时按一小时算。用户余额扣减费用订单状态改为已还车。更新车辆状态为可租归入还车站点。计费这里有个最经典的坑——时长计算。直接用System.currentTimeMillis()相减再除以 60000 看起来没什么问题但如果你后面要统计日均租车时长时区一乱数据就全错了。建议后端统一用LocalDateTime数据库字段也对应DATETIME前端只做展示不做计算所有时间以服务器为准。4.3 后台管理模块和统计报表管理端就是标准的管理系统套路自行车管理、站点管理、用户管理、订单管理都是Controller Service Mapper的增删改查。但这里有两个功能值得做得细一点一个是站点容量校验。站点表里要有capacity字段还车时检查车辆数是否已满满了就提示用户该站点车位已满请前往附近站点还车。要是没这个功能车辆越来越多全都堆在一个站系统就失真了。另一个是数据统计报表。用 ECharts 做一个柱状图展示近 30 天的租车量趋势做一个饼图展示各站点租车占比再做一张表格展示营收排行。后台接口写好之后前端直接对接。这块做出来的效果很直观答辩演示的时候一眼就能看出系统的价值。4.4 定时任务让系统更智能Spring Boot 做定时任务非常顺手核心就两个注解在启动类上写EnableScheduling在方法上写Scheduled(cron 0 0 2 * * ?)。这个系统里定时任务有两个比较自然的应用场景每天凌晨统计前一天的租车数据生成日报存入统计表。定期扫描租赁中订单如果超过 24 小时未还车自动把订单标记为异常推送提醒管理员去线下核实。我能理解有的同学觉得定时任务不是必做功能想砍掉。但我的建议是留着因为这是你在答辩时说系统有工程化能力的凭证而且实现成本很低性价比极高。5. 常见问题与排查技巧实录这个部分我汇总一下我做类似项目时踩过的坑还有平时帮同学看代码时最常见的问题基本覆盖了从开发到部署的全过程。5.1 Spring Boot 版本和依赖冲突前面说了版本问题这里再补充一种情况Maven 依赖冲突。典型表现是启动时报NoClassDefFoundError或ClassNotFoundException但代码本身看起来没问题。排查办法是mvn dependency:tree看依赖树找出哪些依赖被重复引入或者版本不一致。还有一个很隐蔽的坑是 Lombok 和 Java 版本不匹配。你用 JDK 8 但 Maven 里配置的编译插件是 17或者 Lombok 版本太老就会出现getter方法找不到的诡异编译错误。我的习惯是 Lombok 直接上 1.18.24 以上版本省心。本地开发时另一个高频问题是端口被占用。Spring Boot 默认跑在 8080但你机器上可能已经有别的服务占着。别去改代码启动时加参数最省事java -jar xxx.jar --server.port8081或者 IDEA 里在 Edit Configurations 的 Program arguments 里加--server.port8081。5.2 MyBatis 的坑Mapper 扫描和 XML 绑定MyBatis 最常见的报错是Invalid bound statement (not found)。这个错误 90% 的原因是 Mapper 接口和 XML 文件没有对应上。检查三处第一接口的全限定名和 XML 的namespace完全一致第二接口方法名和 XML 里的操作 id 完全一致第三XML 文件有没有被输出到 target 目录。第三点特别容易忽略。如果你把 XML 放在src/main/java目录下Maven 默认不会把它打包到 classes 里。解决办法是在pom.xml的 build 里加资源配置或者直接把 XML 放在src/main/resources/mapper目录下。我推荐后者一劳永逸。还有一个 MyBatis 相关的小细节参数传递。单个参数可以直接用#{id}多个参数就必须加Param(xxx)否则报BindingException。另外 SQL 里if标签里判断参数是否为 null 时参数名要跟Param保持一致别漏了。5.3 日期格式和数据精度问题Spring Boot 返回 JSON 时LocalDateTime默认是一串数字数组前端拿到根本没法展示。解决方式是在配置文件里加spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8但注意这个配置只对java.util.Date生效对LocalDateTime不生效。LocalDateTime需要在字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)或者全局注册 Jackson 的 JavaTimeModule。这个小问题能卡住不少人半天。金额精度那块再强调一遍数据库用DECIMALJava 用BigDecimal进制转换和计算都用 BigDecimal 的方法别用double做金额计算。这不是小题大做是真实的资金安全问题答辩的时候提一句为什么不用 double老师就知道你懂行。5.4 前后端联调CORS 和打包集成本地开发时前端 Vue 在 3000 端口跑后端在 8080 端口跑前端请求后端必然遇到跨域问题。后端写一个 CORS 配置类允许指定来源跨域即可。注意allowedOriginPatterns在 Spring Boot 2.4 之后不能用allowedOrigins(*)带 withCredentials 的组合这是个老版本迁移到新版本时容易踩的坑。部署的时候有一个非常实用的技巧把 Vue 项目npm run build生成的dist目录里的文件直接复制到 Spring Boot 的src/main/resources/static下再重新打包。这样前后端就合并成同一个 jar 包部署时只需要一个java -jar命令就全跑起来了不用单独布 Nginx。毕设演示的时候这一招能让你的部署流程看起来特别干净。6. 项目部署与答辩准备心得6.1 打包部署的完整流程先梳理一遍从代码到可运行的完整流程这一步建议你在答辩前至少完整走两遍检查application.yml里的数据库配置确认连接的是演示用的数据库密码正确。如果前端已经合并到 static直接 Maven 打包mvn clean package -DskipTests。到 target 目录找到生成的 jar 包用java -jar xxx.jar启动。浏览器访问http://localhost:8080先跑一遍核心流程注册、登录、租车、还车、看记录。这里有一个我反复吃亏后总结的经验演示前一定要用脚本把数据库重置一遍。具体做法是写一个.sql初始化脚本里面包含建表语句和演示数据答辩那天早上先执行一遍保证数据干净、时间戳合理。别用你平时调试留下的脏数据去演示到时候订单状态乱七八糟老师一眼就看出来你系统没做数据管理。6.2 演示脚本的设计演示不要照着功能清单一个一个点那太像说明书。我建议你设计一条业务故事线先用一个普通用户账号登录演示个人信息和余额充值。选一个站点查看实时车辆列表演示租车。换一个站点演示还车页面展示费用明细和余额变化。切到管理员账号演示车辆管理和订单查询。最后切到统计报表页展示租车趋势和站点排行。这条线走下来整个系统的业务闭环就在老师面前完整呈现了。期间你要顺手解释关键设计为什么租车要校验用户和车辆双重状态为什么还车要检查站点容量为什么金额用 BigDecimal。6.3 论文写作的几个加分点论文部分我不多展开只点几个容易被忽略的加分点需求分析里画好用例图明确用户、管理员、调度员三种角色。数据库设计章节放 ER 图和表结构说明特别是状态字段的注释要写清楚状态含义。核心功能环节加时序图比如租车和还车的时序图这是老师判断你真做过的重要依据。测试章节不要只写系统功能正常可以加一段接口并发测试的简单分析比如 Jmeter 压测 50 个并发租车请求看系统是否有异常。有数据论文就有说服力。7. 最后的几点实在话这个项目做完一遍我的体会是公共自行车租赁系统表面上看是个毕设题目实际上是一整套业务逻辑的训练场。你处理的不只是增删改查而是一个车辆状态怎么随时间流转、订单怎么在多个条件约束下安全生成、金额怎么在保证精度的前提下被计算扣减的完整过程。最后再分享一个小技巧也是我每次带人做这类系统必说的一句话先做通一条主链路再做完整系统。什么意思就是你第一天不要想着把五张表全建了、十个接口全写了你先用最简单的方式把用户注册 → 登录 → 租车 → 还车 → 计费 → 查记录这六步跑通。哪怕页面丑一点哪怕接口写得不优雅先把闭环打通。闭环通了你的信心就有了后面加什么功能都是增量工作闭环不通就算你写了 20 个接口系统也是散的。这套方法不止适用于这个毕设也适用于你以后做任何新项目。业务永远是第一位的技术只是实现业务的手段。把这句话想明白你做的就不只是一个能过答辩的系统而是一个能让你真正学到东西的项目。
返回列表