每年到了毕业设计季总能看到大量同学在“电动车租赁系统”、“共享单车系统”、“校园二手交易平台”这类题目之间反复横跳。这题目看着平淡无奇但真上手去做从技术选型、数据库设计到联调部署每一步都藏着不少门道。这篇博文我就以“高校电动车租赁系统平台”为例把整个项目的设计思路、核心代码实现、数据库建模、部署流程从头到尾过一遍顺带把那些我在实操中踩过的坑和总结出的经验一并交代清楚希望能给正在做同类课题的同学们一些参考也想让刚接触SpringBoot和Vue全栈开发的朋友知道一个完整的毕业设计项目到底是怎么从零到一落地的。这个项目我用的是SpringBoot Vue MySQL的组合这三个词放到今天的就业市场里属于最务实的一套Java全栈解决方案。SpringBoot负责后端接口和业务逻辑Vue负责前端页面和交互MySQL负责数据持久化。系统本身要解决的场景也很明确高校校园内师生有短途出行需求校方或运营方投放一批电动车供租赁学生通过小程序或者网页完成注册、扫码租车、计费、还车、支付这一整条流程。比共享单车多了个校园封闭场景比传统租车行多了个线上自助化的要求规模不大但五脏俱全非常适合拿来练手和当毕设题目。你可能会问这种项目网上源码一大把为什么还要认真做我的看法是毕设和实战项目的区别在于毕设不仅要求功能跑得通还要求你能把技术点讲清楚让答辩老师觉得你的系统是经过思考设计的而不是盲目抄来的。所以后面我讲的可复制操作不只是贴代码也会把为什么这么设计的逻辑一并拆开。1. 为什么这个题目适合当毕设技术栈又是怎么定的1.1 选题价值和需求场景分析高校电动车租赁这个题目的优势在于它的业务边界非常清晰。需求方是校园内的学生和教职工车辆投放范围是校园及周边租赁时长普遍是几十分钟到半天使用场景是上课、取快递、去食堂、跨校区通勤。相比一个泛泛的“商品租赁系统”它多了一层校园管理的属性比如学生身份认证、车辆状态实时监控、乱停乱放的管理约束。从毕设评分角度来看这类题目在功能完整度、技术难度、论文可写性三方面比较均衡功能上能拆出用户端、管理端、车辆端三个视角技术上能覆盖前后端分离、权限认证、地理位置、支付流程、报表统计论文上每一块都有话可说不至于憋不出字数。我当时拿到这个题目后第一步不是写代码而是花了整整两天做用户角色分析。我把系统的使用人群拆成了三类学生用户、系统管理员、车辆运维人员。学生用户关心的是“附近有没有车、多少钱、怎么还车”系统管理员关心的是“哪辆车被租了、订单多少钱、什么时间段是高峰”运维人员关心的是“哪辆车电量低、哪辆车报修了、需要调度到哪里”。这三种角色对系统功能的需求差异很大能同时覆盖这三类人系统的功能面就已经像样了。1.2 SpringBoot、Vue、MySQL三者是怎么分工配合的很多同学会纠结“SpringBoot和SSM到底选哪个”我的建议非常直接没有特殊要求就用SpringBoot。SpringBoot的自动配置机制把SpringMVC、MyBatis等框架的繁琐配置大量收敛了你只需要通过SpringBootApplication启动一个应用通过application.yml维护数据源和端口剩下的交给框架约定。这对毕设阶段的学生来说友好得多可以省下时间集中处理业务逻辑。Vue端我用的是Vue 2 Element UI的组合这是目前新手最不陌生的方案。组件化开发让页面可以拆成车辆列表、订单卡片、支付弹窗等独立单元视图层逻辑清晰配合Vue Router做页面跳转、Vuex管理登录态和全局数据写出来的代码不会因为项目小就变得凌乱。MySQL在其中承担的核心任务是事务性数据存储。订单、支付流水、车辆状态变更这些操作为什么必须用MySQL而不是直接把数据放内存里因为租赁业务涉及金额结算一旦订单状态和支付状态不一致后续对账就是灾难。MySQL的ACID特性配合SpringBoot里的事务注解Transactional能保证“创建订单扣减余额”这类组合操作要么全部成功要么全部回滚。表结构上我后面会详细画出来这里先有个整体认知就可以。2. 项目整体功能设计与数据库建模2.1 系统模块划分用户端到底要做什么管理端又要管理什么高校电动车租赁系统的功能设计我倾向于分为前台用户端和后台管理端两大块。前台的核心流程是用户注册登录 → 浏览车辆列表 → 查看车辆详情电量、时租金 → 选择车辆并下单 → 模拟开锁/还车 → 支付费用 → 查看历史订单。管理端则是对前台业务的治理车辆信息管理新增车辆、上下架、设定电量阈值、订单管理查询所有订单、处理异常订单、手动结算、用户管理学生认证、禁用账号、计费规则配置设置不同车型的时租金、设置押金金额、数据统计看板日订单量、收入趋势、车辆使用率排行榜。如果想让系统看起来更有论文价值可以再加一个运维模块处理车辆的故障上报和维护记录。我自己的项目里加了车辆维护记录这张表本来是想凑字段结果答辩时老师专门夸了这点说能考虑到车辆全生命周期的状态管理说明不是单纯做了一个交易网站。2.2 数据库表结构设计五张核心表与字段定义数据库设计是这个项目里最值得讲的一部分。我建了这些表用户表user、车辆表vehicle、订单表rental_order、支付流水表payment_record、计费规则表charge_rule、维护记录表maintenance_record。下面挑核心的几张表讲。用户表字段id、username、password加密存储、real_name、student_no、role1-用户 2-管理员 3-运维、phone、balance账户余额、status、create_time。密码用BCrypt加密保存禁止明文这是很多毕设容易丢分的地方。balance字段用来做预充值消费租赁结束后从余额扣款比每次单独调支付接口更适合校园场景。车辆表字段id、vehicle_no、name、type车型、battery电量百分比、status0-空闲 1-使用中 2-维护中 3-已下架、longitude、latitude、price_per_hour、park_location、create_time。其中status字段是整个系统状态流转的核心所有业务操作都在围绕着这个字段的变更展开。订单表字段id、order_no业务单号、user_id、vehicle_id、start_time、end_time、duration_minutes、total_amount、status0-进行中 1-已完成 2-已取消 3-异常、pay_status。订单表的设计要注意索引user_id和status经常作为查询条件必须加普通索引否则数据量一上来查询就会变慢。支付流水表字段id、order_id、user_id、amount、pay_type余额/微信、trade_no、status、create_time。这张表的意义在于留痕。学生论文里写“实现了支付功能”的时候如果只有订单表里的pay_status字段不好证明资金流转的完整性。单独拆一张流水表既有技术价值也有审计价值。在定字段类型的时候有两点特别提醒金额一律用DECIMAL(10, 2)不要用FLOAT或DOUBLE因为浮点数在数据库里是近似值金额算错了很难查。时间字段用DATETIME别用TIMESTAMP否则2038年问题且带时区处理容易出幺蛾子。车辆位置字段如果追求精度可以用POINT类型配合空间索引但毕设阶段用DECIMAL(9, 6)存经纬度完全够用。3. 后端核心业务实现与关键代码解析3.1 项目结构划分与统一返回体设计后端我用的包结构是标准的controller / service / mapper / entity / common五层。entity对应数据库表实体mapper是MyBatis-Plus的数据访问层service写业务逻辑controller只做参数接收和结果返回common放全局异常处理、统一返回结果、JWT工具类。很多同学喜欢把逻辑直接写在controller里图省事但答辩的时候老师一问“你这个模块的复用性怎么样”就露馅了。统一返回体我在项目一开始就定义好了这也是SpringBoot项目的常规操作Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.message success; result.data data; return result; } public static T ResultT error(String message) { ResultT result new Result(); result.code 500; result.message message; return result; } }有了这个统一返回体前端在封装Axios的时候就可以统一处理状态码不需要对每个接口单独做判断。我见过一些项目每个接口返回的字段名都不一样有的返回data有的返回result前端联调的时候那叫一个痛苦。从设计规范上讲统一返回体是团队协作的基本功放在毕设里也很加印象分。3.2 基于JWT的登录认证与权限控制高校电动车租赁系统涉及三种角色接口必须有权限控制。我用的方案是JWT SpringBoot拦截器。用户登录成功后后端签发一个有效期2小时的Token包含userId和role信息前端拿到Token存到localStorage里每次请求在请求头里带上Authorization: Bearer token。后端的拦截器里只需要校验Token是否有效、当前用户是否有权访问对应接口。核心代码如下Component public class JwtInterceptor implements HandlerInterceptor { Autowired private JwtUtil jwtUtil; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口 if (request.getRequestURI().contains(/user/login) || request.getRequestURI().contains(/user/register)) { return true; } String token request.getHeader(Authorization); if (StringUtils.isBlank(token) || !token.startsWith(Bearer )) { throw new RuntimeException(未登录或登录已过期); } String userInfo jwtUtil.parseToken(token.replace(Bearer , )); if (userInfo null) { throw new RuntimeException(Token无效); } // 将用户信息存入request方便后续业务获取当前用户 request.setAttribute(userId, userInfo); return true; } }这里有一个比较关键的细节Token里放了userId之后业务代码里千万不要再去查询用户是否存在。因为用户可能被管理员删除了但Token还没过期这种情况应该直接拦截。我是通过Redis或者数据库查一次来校验的但很多项目在校验上偷懒导致接口能操作一个不存在的用户下的数据后期排查很费劲。JWT的好处是服务端无状态多台服务器部署时不需要共享Session存储缺点是Token无法主动失效。毕设场景下这个缺点不明显但我在项目里还是加了“用户修改密码后强制重新登录”的逻辑做法是登录时记录一个token_versionJWT里带上拦截器比较版本号不一致就拒绝。3.3 租赁核心流程下单、计费、还车的链路设计租赁业务的核心链路是用户选择空闲车辆 → 创建订单订单状态置为进行中车辆状态置为使用中 → 用户点击“还车结算” → 计算时长与金额 → 扣减余额 → 更新订单状态与车辆状态。下单接口的实现我用了事务控制因为“创建订单”和“修改车辆状态”必须同时成功或同时失败否则会出现车被租走但订单没建成的严重问题Transactional(rollbackFor Exception.class) public RentalOrder createOrder(Integer userId, Integer vehicleId) { // 1. 查询车辆判断状态 Vehicle vehicle vehicleMapper.selectById(vehicleId); if (vehicle null || vehicle.getStatus() ! 0) { throw new RuntimeException(该车辆不可租赁); } // 2. 生成唯一订单号 String orderNo R System.currentTimeMillis() RandomUtil.randomNumbers(4); // 3. 创建订单 RentalOrder order new RentalOrder(); order.setOrderNo(orderNo); order.setUserId(userId); order.setVehicleId(vehicleId); order.setStartTime(LocalDateTime.now()); order.setStatus(0); order.setPayStatus(0); rentalOrderMapper.insert(order); // 4. 修改车辆状态为使用中 vehicle.setStatus(1); vehicleMapper.updateById(vehicle); return order; }计费逻辑是另一个容易出错的点。计费规则要支持“按小时计价”和“按分钟计价”两种模式而且不足一小时的场景很多。我的设计是订单创建时活取车型的price_per_hour还车时把分钟数除以60得到小时数不足15分钟的部分按15分钟计算向上取整。用代码表示就是Math.max(1, (int)Math.ceil(durationMinutes / 15.0)) * (pricePerHour / 4)这样半小时就是两格20分钟也是两格不会出现比实际便宜的情况。如果你在论文里写“支持押金功能”建议同时实现“免押金学生认证”流程。就是在用户信息里加一个is_verified字段认证过校园卡的用户可以免押金租车没有认证的需要冻结一定金额。这里的冻结金额不是真的扣钱而是在下单时锁定余额还车后再解冻我用了一张deposit_record表来追踪比直接把balance减掉再还回来更清晰。3.4 MyBatis-Plus的用法和那些提升开发效率的配置数据层的技术选型我用了MyBatis-Plus而不是原生MyBatis最大原因是开发效率高。单表查询完全不用写SQL通过QueryWrapper就能搞定分页插件也是开箱即用Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setMaxLimit(500L); interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; } }分页查询是管理端列表页的标配需求顺序不能乱。先配置拦截器然后在Service里用PageT对象接收参数public PageResultOrderVO getOrderPage(OrderQuery query) { PageRentalOrder page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperRentalOrder wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.isNotBlank(query.getStatus()), RentalOrder::getStatus, query.getStatus()) .orderByDesc(RentalOrder::getCreateTime); rentalOrderMapper.selectPage(page, wrapper); // 类型转换将实体转成VO补充用户名、车辆名称等信息 return buildPageResult(page); }这里有个从实战中总结的教训分页查询返回的数据尽量不要把实体类直接返回给前端。比如订单列表里前端需要看到的是用户姓名而不是user_id需要看到车辆品牌型号而不是vehicle_id。就为这个需求我专门定义了VO类查询完后再做一次字段映射。可能有人觉得多此一举但是当你把系统做完回头改需求的时候就知道VO层和实体层分离有多重要了。否则前端每要一个字段你就得改动数据库表结构或者实体类这是很糟糕的维护体验。4. 前端Vue实现细节与接口联调要点4.1 前端工程化结构与路由设计前端我用Vue CLI脚手架生成的项目主要目录分成views页面、components公共组件、router路由、storeVuex、api接口请求。为什么要把接口请求单独建一个目录因为在实际项目中同一个接口会被多个页面调用如果每个页面都写一次axios请求万一后端接口变了你就要全局搜索替换。把所有请求集中到一个对于管理是很好的维护习惯。路由设计上我用了路由懒加载通过component: () import(...)的方式引入页面组件这样首屏加载时不会把所有页面都打进同一个bundle里页面打开速度会快不少。同时配置了全局前置守卫做登录校验router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) } else if (token to.path /login) { next(/) } else { next() } })如果你用了Vuex管理用户信息建议在路由守卫里同时检查用户信息是否已加载避免刷新页面后Vuex里userInfo丢失导致页面数据显示异常。这是我做联调时碰到的经典问题登录后一切正常一刷新页面就跳回登录页排查了好一会儿才发现是Vuex的 store 在刷新后会被重置需要配合localStorage持久化或者重新调获取用户信息接口。4.2 Axios封装与跨域问题处理前端请求后端接口第一个拦路虎就是跨域。开发环境下我用的是Vue CLI的代理方案在vue.config.js里配置module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }后端接口地址统一以/api开头代理到后端服务时去掉前缀这样前端代码里请求的都是相对路径不写死IP和端口。为什么这套能解决问题因为跨域限制是浏览器的行为开发环境下请求先发到Vite或者webpackDevServer同源地址再由服务端转发浏览器感知不到。axios封装方面我在request拦截器里统一放Token在response拦截器里统一处理错误码。后端返回的code如果不是200就直接弹出错误提示如果是401或Token过期就清空登录状态并跳转到登录页。这套机制能让你在联调阶段节省大量时间。4.3 Element UI表单校验与页面组件化实践管理端的车辆管理页面是我觉得最值得讲的它涵盖了表格展示、新增修改弹窗、表单校验、删除确认这一整套标准交互流程。Element UI的表格组件通过el-table绑定数据配合el-pagination做分页。新增和修改共用同一个弹窗组件通过判断是否有id来决定调用的接口。表单校验规则配置在data里比如车辆编号必填、价格必须大于0提交前调this.$refs.form.validate()。用户端的租车页面则需要多一点设计比如用卡片展示车辆状态绿色代表空闲、灰色代表维护中、黄色代表使用中。电量展示用了el-progress组件圆形进度条直观呈现剩余电量。在选择车辆时我用了一个地图插件的简化版在校园地图上标注车辆位置这样用户看起来比纯列表更有体验感。前端页面里我特别重视的一件事是“空状态展示”。列表没有数据时展示一张空状态的插图和提示文字而不是干巴巴的空白页。这个细节虽然简单但在答辩演示时很讨喜老师会觉得你考虑问题全面。5. 本地启动、项目打包与服务器部署全流程5.1 本地环境准备JDK、Node、MySQL一个都不能少在开始写代码之前环境必须一次配好不然后面处处踩坑。我建议的阵容是JDK 1.8SpringBoot 2.x兼容性最好别一上来就整JDK 17、Node 14或16、MySQL 5.7或8.0。这里特别说明SpringBoot 2.6.x对应Java 8没问题但如果你用SpringBoot 3.x就必须上Java 17很多老教程根本不适用所以版本匹配一定要留心。MySQL安装后要检查默认字符集项目初始化时我执行了这样一条命令CREATE DATABASE IF NOT EXISTS ev_rental DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;为什么要强调utf8mb4因为utf8在MySQL里存不了emoji表情和一些生僻字大学生用户昵称里表情符号出现概率极高不用utf8mb4很容易在写入时报Incorrect string value错误。5.2 初始化数据库脚本与后端配置文件的连接细节数据库初始化我准备了一个init.sql包含建表语句和基础数据管理员账号、示例车辆、计费规则。这样做的好处是项目换了一台新电脑也能一键恢复环境尤其是给答辩老师演示的时候不会因为环境问题掉链子。后端的application.yml配置要注意时区和服务端口server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ev_rental?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true这个配置非常关键它能让数据库的user_id自动映射成Java的userId少写大量XML映射。日志输出配置成StdOutImpl方便开发阶段在控制台看SQL语句。等部署上线前记得把日志级别调到warn否则生产环境控制台会疯狂打印SQL影响性能。5.3 前端打包与SpringBoot构建发布前端开发完成后需要执行npm run build产物会生成在dist目录。这时你需要决定部署方式我推荐的是最省事的方案把dist目录里的静态文件复制到SpringBoot的src/main/resources/static目录下然后重新打包成单个jar包。这样你在服务器上只需要启动一个进程前后端都齐了管理成本极低。后端打包命令mvn clean package -DskipTests打包完成后target目录下会有一个ev-rental-0.0.1-SNAPSHOT.jar文件。把这个文件传到服务器上执行nohup java -jar ev-rental-0.0.1-SNAPSHOT.jar app.log 21 用nohup的目的是让进程在SSH会话断开后依然存活。这个细节很重要很多同学部署时直接在终端前台跑jar包一关终端服务就停了答辩前一晚调试到凌晨也不想再经历一次。如果想让系统看起来更“产品化”可以在服务器上安装一个Nginx把80端口指向SpringBoot的静态文件目录并做反向代理到8080端口。Nginx配置也不复杂server { listen 80; server_name localhost; location / { root /opt/ev-rental/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files这行是前端路由History模式的关键不然你在页面上点刷新按钮就会得到404。这也是我在部署时遇到的经典问题后面会详细说。6. 开发过程中最常踩的坑与排查经验总结6.1 数据库连接报错时区问题和驱动版本问题在项目启动阶段最常见的报错就是数据库连接失败。很多同学用的连接串是jdbc:mysql://localhost:3306/ev_rental然后遇到一个“The server time zone”的警告。这个警告的背后是MySQL 8.0以上的版本默认时区跟JDBC驱动不一致解决方法是url上面加serverTimezoneAsia/Shanghai。另外MySQL 8.0需要使用com.mysql.cj.jdbc.Driver而不是老版的com.mysql.jdbc.Driver如果用的驱动版本过旧还会直接报“ClassNotFoundException”。排查这个问题的思路是分步骤确认先用Navicat或者命令行确认MySQL服务已启动并能登录再确认数据库和账号权限没问题最后才怀疑连接串配置问题。而不是上来就卸载重装MySQL那样只会更加绝望。6.2 前后端联调时的跨域与Token失效问题跨域问题在开发环境下已经用代理解决了但部署到服务器之后如果不做Nginx代理就会出现新的跨域报错。因为浏览器访问的是Nginx的80端口后端跑在8080端口前端的请求头里带了TokenNginx默认可能不转发请求头。解决方案就是我前面写的在Nginx配置里加上proxy_set_header Authorization $http_authorization;。一定要知道这种“打包后打不开”的问题90%是Nginx配置或static路径不对而不是代码逻辑问题。Token失效的问题也很烦。JWT里默认不设置过期时间的话Token永远不会失效这在安全上是个隐患。我当时设置了2小时过期用户在还车结算时发现Token已过期直接报错弹回登录页。我的解决方案是在支付接口和还车接口里做一个Token自动续期的逻辑只要用户还在操作就给他重新签发一个过期时间更晚的Token。这样既保证了安全性又不会太影响用户体验。6.3 订单金额计算错误浮点数问题与精度问题计费模块如果直接用double类型计算金额在Java里会得到一个很像“0.30000000000000004”的数。查到支付流水时发现这种数字只能是DB字段类型设计时没注意浮点数精度。我用Decimal类型后来才明白金额计算要用BigDecimal或者让MySQL直接算。推荐方案是先用分钟数乘以BigDecimal.valueOf(pricePerHour).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP)得出金额再做数据库写入。如果你在论文里批判性地提到“double计算金额会有精度问题所以我选择了Decimal”答辩老师会觉得你有实践敏感性。这种细节是拉开档次的地方虽然只是几行代码的事但体现了你是不是真的理解了项目的每一处角落。6.4 前端刷新404和页面上线后样式不生效部署之后前端页面刷新404这个问题我在前面已经说了根本原因是Vue Router的History模式依赖服务器端配合。解决方法是Nginx的try_files配置或者换成Hash模式mode: hash后者不用配置服务器但对某些评委来说会显得技术选型不够前沿。我更建议上手就把Nginx配置写对这才是真实项目里会遇到的场景。还有一个打包相关的问题前端dist目录里引用的静态资源路径是绝对路径/js/app.js如果你部署在子路径下就会白屏。解决办法是在vue.config.js里设置publicPath: ./让所有资源引用变成相对路径。6.5 毕业设计论文相关的配套准备最后聊一些技术之外但影响“生死”的部分。论文结构上我建议是绪论背景意义、国内外现状、相关技术介绍SpringBoot、Vue、MySQL、系统分析可行性、需求分析、用例图、系统设计架构图、功能模块图、数据库E-R图、系统实现核心功能讲解配截图、系统测试测试用例表与结果分析、总结。如果你想让项目更容易过审建议在测试阶段不要只做“功能能跑通”的验证而是写一份简单的测试用例表把测试步骤、预期结果、实际结果列清楚。比如“用户选择空闲车辆下单”预期结果是“订单生成、车辆状态变为使用中”实际结果打勾。这个表格能在很大程度上说明你的系统做过系统的验证而不是随随便便交了份作业。数据库设计这一部分画E-R图的时候可以用draw.io或者ProcessOn把实体关系画清楚。用户和订单是1对多车辆和订单是1对多用户和支付流水是1对多订单和支付流水是1对1。车辆和订单之间是否要建立外键我可以多说一句毕设里建议加外键约束这样数据一致性有保证虽然在大型互联网项目中为了性能会去掉外键但写论文时外键存在更有“数据库设计规范”的说服力。7. 写在最后的经验之谈做毕业设计这几个月我最大的体会是一个杂乱的系统之所以看起来“像作业”往往不是因为功能少而是因为细节没有打磨到位。同一个项目有的人交了源码就完事有的人整理出完整的部署文档、数据库脚本、测试用例和演示视频后者的评分通常都比前者高一档。技术能力在这个阶段固然重要但认真和规范可能更重要。如果这个项目你想在答辩之后继续扩展我建议优先考虑三个方向一是接入微信小程序端把车辆定位和扫码开锁放到小程序里这会让系统更接近真实产品二是引入Redis做热点缓存解决高峰期车辆位置和订单状态的查询压力三是把支付对接换成微信支付的沙箱环境这个在论文里写出来含金量会明显提升。我自己在做类似课程设计和毕业设计时一直坚持“先想清楚再动手先跑通再优化”的原则。前期花在需求分析和数据库设计上的时间都会在后面的编码阶段以数倍的效率返还给你。如果你正卡在某个技术点上或者在这个项目的某个环节上反复折腾过欢迎在评论区交流我也很乐意把自己踩过的坑再详细复盘一遍。