资讯动态

SpringBoot汽车租赁系统毕设:业务梳理、数据库设计与答辩全解析

发布时间:2026/9/10 18:14:35 来源:尧图企业网站定制
又到了毕设产出集中的节点论坛和社群里隔三差五就有人问汽车租赁系统的 SpringBoot 毕设源码有没有现成的能跑起来直接交我的回答一直是能跑起来的源码到处都是能扛住评阅老师追问的源码才是真的值钱。这几年帮人评审过的毕设项目里做得扎实的往往不是功能最多的而是业务逻辑最清楚的。你从网上拉下来的每个项目最怕的不是有bug而是根本讲不明白它为什么这么设计。这篇文章我不打算给你贴一套完整代码充字数而是想跟你聊清楚“基于SpringBoot的汽车租赁系统”这类毕设从业务梳理、技术选型、数据库设计到核心代码实现的完整链路。你拿到源码之后对照这篇文章拆一遍能动的部分动一动能讲的部分讲明白比单纯改个包名就交上去要稳妥得多。1. 先拆业务骨架汽车租赁系统到底在解决什么问题1.1 一条订单的完整生命周期很多同学上手就写代码写到订单表就卡住了因为没想清楚汽车租赁的本质业务流程。说到底这是一个“资源—预约—履约—结算”的闭环业务。我习惯把一个完整的租车流程画成下面这条线这样无论代码怎么组织心里都有一个地图用户注册登录 → 管理员在后台发布车辆 → 用户浏览/搜索车辆 → 用户选定租期、提交租车订单 → 管理员审核订单 → 审核通过后用户按期取车 → 用户用车 → 用户还车 → 系统根据租期和超时情况计算费用 → 订单完成这中间有一条容易被忽视的并行线管理员对车辆状态的管控。车辆不是永远可租的它在订单审核通过的那一刻起就应该变为“已租”否则就会出现同一辆车被两个人同时下单的尴尬局面。这条车与订单之间的状态联动是整个系统最核心的部分。把流程理解成一条线之后你会发现所谓的“设计与实现”其实就是把这条线上每一站的输入、输出、异常情况搞清楚再用代码表达出来。1.2 角色与功能清单哪些必须有哪些只是凑数汽车租赁系统通常是双角色的管理员和普通用户。功能上我建议分两类一类是“没有就说不过去”的核心功能一类是“有则锦上添花”的辅助功能。核心功能必须在五天以内能做完且做稳辅助功能看剩余时间再决定。先看用户端的核心功能注册与登录含密码加密存储车辆列表浏览与关键字/品牌筛选车辆详情页下单租车选择取车时间、还车时间我的订单列表全部、待审核、租赁中、已完成等状态筛选取消订单只在待审核状态下允许用户端的辅助功能个人资料修改、押金充值记录、通知公告查看、租车评价。这些有时间就做没时间不做也能交差。再看管理端核心功能管理员登录车辆管理新增车辆、编辑信息、下架、上架车辆列表含条件分页查询订单审核通过/拒绝拒绝要填原因订单管理查看所有订单标记还车并结算用户管理查看用户列表禁用/启用用户管理端的辅助功能数据统计每日订单量、收入汇总、图片上传、公告管理。数据统计这块如果第四五章的技术点吃透了做起来并不难但如果时间紧张留着不做也不影响主线逻辑。这套功能清单列出来之后你会发现它和酒店管理、图书管理、设备管理系统高度相似。本质上都是“资源 预订 状态管理”只是资源的字段不同而已。你把这个共性想通了后面看代码的时候就不会被各种业务细节带跑。2. 技术选型定生死为什么我推荐SpringBoot 2.7.x这一套组合2.1 版本怎么选别追新毕设项目的第一原则是求稳第二原则是资料多。如果你是用搜到的问题去排错资料多的版本能让你少掉一半头发。我建议你选这一套组合这是现阶段最稳的组件推荐版本原因JDK1.8 或 11大多数学校和教材还在用兼容性最好SpringBoot2.7.x资料极其丰富能覆盖你遇到的绝大多数报错MyBatis-Plus3.5.x单表CRUD几乎不用写SQL省时省力MySQL5.7 或 8.0两者皆可8.0记得用8.0的驱动和URL参数Maven3.8稳定即可前端方案Thymeleaf / BootstrapjQuery / Vue按你的前端水平三选一后面第四章细说SpringBoot 3.x虽然已经是很成熟的版本了但要求JDK17起步很多配套的依赖、博客教程和异常解决方案还停留在2.x时代。你花一个下午在版本适配上面不如把这时间留给业务代码。记住毕设的核心是业务逻辑完整、设计思路清晰而不是用了最新版本。2.2 创建项目失败的头号大坑start.spring.io 拉不动每次听到有人说“IDEA创建SpringBoot项目超时”我第一反应就是网络问题。Spring官方的start.spring.io在国内的访问速度很不稳定尤其下午高峰时段很容易卡在加载页面。解决方案有三个按推荐顺序排改用阿里云镜像在 IDEA 的 HTTP Proxy 或 Server URL 里填https://start.aliyun.com。这个镜像和官方结构几乎一样速度稳定。手动建了一个普通Maven项目如果在IDE里始终创建失败干脆不要用脚手架。新建一个Maven空项目在pom.xml里手动写入spring-boot-starter-parent、spring-boot-starter-web、mybatis-plus-boot-starter、mysql-connector-java等依赖再写一个启动类。这招最笨但绝对不会被卡住。换网络环境如果公司或学校网络有代理限制创建超时也可能是代理设置的问题。在IDEA的Settings → Appearance Behavior → System Settings → HTTP Proxy里改成Auto-detect或者关掉代理重试。依赖下载慢是另一个问题在settings.xml里加阿里云镜像是最常规的操作。Maven的仓库地址配置好了后面所有依赖都是一次性拉完的。2.3 包结构怎么摆决定你后面改代码的心情包结构是个容易被忽视但后期影响巨大的事情。我见过不少源码所有类堆在一个包下面controller、service、mapper混在一起光找文件就要半天。我推荐一个比较干净的划分也是我最常用的方案com.example.carrental ├── CarRentalApplication.java // 启动类 ├── common // 通用工具与类 │ ├── Result.java // 统一返回体 │ ├── ResultCode.java // 状态码枚举 │ └── BizException.java // 自定义业务异常 ├── config // 配置类 │ ├── MybatisPlusConfig.java // 分页插件配置 │ ├── WebMvcConfig.java // 拦截器/跨域/静态资源映射 │ └── PasswordConfig.java // BCrypt编码器Bean ├── controller // 接口层 │ ├── UserController.java │ ├── CarController.java │ └── OrderController.java ├── service // 业务层 │ ├── UserService.java │ └── impl │ └── UserServiceImpl.java ├── mapper // MyBatis-Plus的Mapper接口 ├── entity // 数据库实体类 ├── dto // 接收前端参数的类 ├── vo // 返回给前端的视图对象 └── interceptor // 登录拦截器这个结构的关键点在于entity和vo分开。很多项目图省事直接拿实体类返回给前端结果密码字段全暴露了还不方便加一些额外字段比如车辆图片的完整地址。分开写虽然多几个类但后期改接口的时候就知道有多爽了。3. 数据库设计订单表是整个系统的定海神针3.1 三张核心表把主体数据落稳数据库设计的原则是先定核心实体再扩展辅助表。汽车租赁系统的核心实体就是三个用户、车辆、订单。辅助表可以有公告、车型分类等但都是可选项。用户表user重点字段如下id主键自增username用户名唯一索引password加密后的密码不要存明文phone手机号id_card身份证号做毕设可存可不存留着体现“租赁需要实名”的业务合理性status状态1正常0禁用deleted逻辑删除标记0未删1已删车辆表car重点字段如下id、car_no车牌号唯一、brand品牌、model车型price_per_day日租金用decimal(10,2)deposit押金用decimal(10,2)status车辆状态1可租2已租0下架image车辆图片地址description车辆描述TEXT类型订单表order这张表是核心中的核心id、order_no订单编号可以用时间戳加随机数生成user_id用户外键car_id车辆外键plan_begin_time计划取车时间plan_end_time计划还车时间actual_end_time实际还车时间还车时更新total_price总费用decimal(10,2)deposit订单当时的押金额度从车辆表冗余拷贝过来因为车以后可能调价status订单状态0待审核1已通过2已拒绝3租赁中已取车4已完成已还车结算5已取消audit_remark审核备注管理员拒绝时填create_time、update_time创建和更新时间这里有一个容易被忽略的细节订单里的deposit和price_per_day为什么要冗余一份因为车辆表里的价格是可以变动的而订单一旦生成费用就应该锁定。如果不冗余过几天车辆调价了这笔订单的费用就算不清楚了。这是很典型的业务设计考虑。3.2 状态流转图订单的6个状态怎么走我用文字把状态流转理一遍用户提交订单 →待审核(0)管理员审核通过 →已通过(1)同时车辆状态改为已租(2)管理员审核拒绝 →已拒绝(2)订单流程结束用户取消 →已取消(5)只能在待审核(0)状态下操作用户取车取车动作可由管理员确认→租赁中(3)用户还车管理员确认 →已完成(4)同时车辆状态改回可租(1)这个状态机的核心约束有两条第一待审核状态是取消订单的唯一起点。已经通过的订单不能随意取消只能走完租赁流程。这个约束要在Service层做校验。第二订单状态和车辆状态必须联动变更。审核通过时改车为已租还车完成时改车为可租。这一步需要加事务否则会出现订单是已通过、车却是可租的不一致状态。后面第6章我还会专门讲事务的坑。3.3 状态字段用数字还是字符串照着上面写就好。数据库里存整数代码里定义一个OrderStatusEnum枚举类把每个数字对应的含义写清楚。这样数据库查询高效代码可读性也好前后端约定的字段值就是整数。用字符串pending、approved也不是不行但会白白增加存储成本和比较开销没必要。建表时给order表的user_id和car_id加上普通索引给car表的brand加上普通索引。这样分页查询和筛选时能走索引虽然毕设数据量小可能感觉不到差异但评阅老师问到索引设计时你能答出来这是加分项。4. 核心模块实现从车辆发布到还车结算的完整闭环4.1 前端方案怎么选这是很多人的盲区先解决一个前置问题这个系统要不要做前后端分离我观察到的现象是很多同学被“前后端分离”这四个字绑架了明明没有Vue基础硬要搭一个Vue3 Element Plus的前端结果光是npm依赖和跨域问题就耗了三四天。我的建议很直接如果你前端基础弱用Thymeleaf 服务端渲染这是最稳的方案。SpringBoot自带模板引擎能直接在HTML里写Java语法一样的东西不用跨域、不用考虑token传参代码量最少。如果你想界面好看一点用Bootstrap jQuery Ajax后端只提供JSON接口。前端页面还是放在static目录下用Ajax请求后端接口。这种方式前后端也算分离但没有工程构建的复杂度。如果你Vue有一定基础或者想挑战一下再考虑真正的前后端分离Vue单独工程通过反向代理或CORS解决跨域登录用JWT。做成了是加分项做不成就会拖累进度。对于大多数希望顺利毕业的同学我推荐第二种或第三种之间的平衡后端统一返回JSON前端用Bootstrap写页面。这样后端接口可以单独用Postman测前端页面也不会太丑。4.2 车辆管理管理员的核心战场管理员的车辆管理本质是对car表做增删改查。但有几个细节需要注意。新增车辆时车辆图片是一个处理难点我一开始是后端只接收图片地址让管理员填一个外链URL这样最简单。如果想让体验更好就用MultipartFile上传到本地目录再把访问地址存数据库。Upload File 保存路径要用配置项不能写死。车辆列表分页查询用MyBatis-Plus的分页插件GetMapping(/admin/car/list) public Result carPage(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword) { LambdaQueryWrapperCar wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(keyword), Car::getBrand, keyword) .or(StringUtils.hasText(keyword)) .like(StringUtils.hasText(keyword), Car::getModel, keyword); wrapper.orderByDesc(Car::getCreateTime); PageCar page new Page(pageNum, pageSize); return Result.success(carMapper.selectPage(page, wrapper)); }这里用LambdaQueryWrapper而不是QueryWrapper好处是类型安全字段名写错了会在编译期就报错而不是运行期才炸出来。车辆上下架直接改status字段就行核心是把状态值约定好。4.3 用户下单并发校验这一行代码是灵魂用户下单的核心逻辑分三步校验、写订单、改车辆状态。对于毕设来说最容易写出问题的是校验这一步。给大家看一个我把并发校验做对的版本Transactional(rollbackFor Exception.class) public Result createOrder(OrderCreateDTO dto) { // 1. 校验车辆存在且可租 Car car carMapper.selectById(dto.getCarId()); if (car null || car.getStatus() ! 1) { throw new BizException(车辆不存在或已被租出); } // 2. 生成订单 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(dto.getUserId()); order.setCarId(car.getId()); order.setPlanBeginTime(dto.getPlanBeginTime()); order.setPlanEndTime(dto.getPlanEndTime()); order.setDeposit(car.getDeposit()); order.setStatus(0); orderMapper.insert(order); // 3. 将车辆状态从可租(1)改为已租(2) int rows carMapper.update(null, new LambdaUpdateWrapperCar() .eq(Car::getId, car.getId()) .eq(Car::getStatus, 1) .set(Car::getStatus, 2)); if (rows 0) { throw new BizException(车辆已被抢租请换一辆); } return Result.success(order.getId()); }请特别注意第3步这一段。如果按很多人的习惯写法先selectById查到车辆判断是可租然后updateById改状态这在单线程下没有问题但两个用户同时请求时两个请求都能查到同一个“可租”状态然后都执行更新就会出现超租超卖。解法是把这个状态判断放进update的 where 条件里用数据库的行锁保证原子性——更新影响行数为0说明条件不满足了直接抛业务异常。这个点如果能在答辩时讲出来是很扎实的一个亮点。4.4 审核与还车结算费用计算必须在后端做管理员审核订单时通过就把状态改成已通过并确保车辆状态已经是已租拒绝则填备注并改成已拒绝。这里同样要有事务和状态校验。还车结算是整个系统业务味最浓的部分。我的结算规则是这样设计的租用天数为计划还车时间减去计划取车时间按天向上取整基础费用 日租金 × 租用天数若实际还车时间晚于计划还车时间超时部分按小时计费每小时费用 日租金 ÷ 24总费用 基础费用 超时费用押金在还车后自动退回毕设里标记一下即可计算金额时必须用BigDecimal绝不能用double。浮点数运算在金额场景下会出精度问题比如0.1 0.2在二进制浮点里并不等于0.3。用上BigDecimal并用ROUND_HALF_UP保留两位小数才是规范做法。public BigDecimal calculateTotalPrice(BigDecimal pricePerDay, LocalDateTime planBegin, LocalDateTime planEnd, LocalDateTime actualEnd) { // 租用天数向上取整 long days ChronoUnit.DAYS.between(planBegin, planEnd); BigDecimal basePrice pricePerDay.multiply(BigDecimal.valueOf(days)); // 超时费用晚还部分按小时计算 BigDecimal totalPrice basePrice; if (actualEnd ! null actualEnd.isAfter(planEnd)) { long hours ChronoUnit.HOURS.between(planEnd, actualEnd); if (actualEnd.plusHours(-hours).isBefore(planEnd)) { hours hours 1; // 不足一小时按一小时算 } BigDecimal hourPrice pricePerDay.divide(BigDecimal.valueOf(24), 2, RoundingMode.HALF_UP); totalPrice basePrice.add(hourPrice.multiply(BigDecimal.valueOf(hours))); } return totalPrice.setScale(2, RoundingMode.HALF_UP); }这段代码里超时部分我把“不足一小时按一小时算”的规则也写进去了。这种细节平时测试不容易发现但评阅老师问起来你能说出自己的计费规则就已经说明你懂业务了。5. 技术亮点集中落实登录鉴权、统一返回体和自动化特性5.1 统一返回体和全局异常代码立刻有了工程味我见过不少项目Controller返回什么类型的都有有的返回Map有的直接返回实体类有的返回String。这样写一时爽前端对接的时候就要哭了。我习惯定义一个统一返回体ResultT包含code、message、data三个字段再配一个静态方法public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(success); r.setData(data); return r; } public static T ResultT error(Integer code, String message) { ResultT r new Result(); r.setCode(code); r.setMessage(message); return r; } }配合全局异常处理RestControllerAdvice业务层直接throw new BizException(车辆不存在)前端就能拿到统一的错误结构。Controller里不再出现try-catch代码清爽很多。这是很低成本的工程化优化但对项目的可维护性提升是立竿见影的。5.2 JWT 拦截器前后端分离下的登录状态管理登录方式现在的主流选择是JWT。原理不复杂就是一个包含用户信息的Base64编码串用密钥做签名每次请求时带在请求头里后端验签后就能知道是哪个用户。JWT的特点是服务端无状态所以后端不需要存session。对于毕设来说需要处理的完整链路是用户登录成功后根据userId和username生成token返回给前端前端把token存起来每次请求放在Authorization请求头里后端自定义一个拦截器拦截/api/**下的请求除了登录、注册、车辆列表这几个白名单接口拦截器里解析token如果解析失败或过期直接返回401未登录的JSON关键代码在拦截器里public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行预检请求 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token null || token.isEmpty()) { response.setStatus(401); response.setContentType(application/json;charsetutf-8); response.getWriter().write({\code\:401,\message\:\未登录\}); return false; } try { Claims claims Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); request.setAttribute(userId, claims.get(userId)); return true; } catch (Exception e) { response.setStatus(401); response.setContentType(application/json;charsetutf-8); response.getWriter().write({\code\:401,\message\:\登录已过期\}); return false; } } }解析用户信息这一步我习惯用request.setAttribute存起来Controller里再从request里取出userId。相比用ThreadLocal这种方式更简单写起来也不容易错。5.3 MyBatis-Plus 三板斧逻辑删除、自动填充、分页插件MyBatis-Plus是毕设提速的神器但有三个配置你得知道不然用起来会有“怎么不生效”的困惑。逻辑删除是第一个。在实体类的deleted字段上标注TableLogicBaseMapper的删除操作会自动变成更新操作查询时自动带上deleted0的条件。这样用户删除后还能在后台看到痕迹不会真的把数据删没。配置逻辑删除后注意所有自定义SQL里也要手动加条件否则会有隐患。自动填充是第二个。在create_time和update_time字段上标注TableField(fill FieldFill.INSERT)和TableField(fill FieldFill.INSERT_UPDATE)再实现一个MetaObjectHandler的配置类插入和更新时自动填充时间。省去每次手动 set 时间的步骤。分页插件是第三个。MyBatis-Plus的分页功能不是默认开启的要在配置类里注册Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor pagination new PaginationInnerInterceptor(DbType.MYSQL); pagination.setMaxLimit(200L); interceptor.addInnerInterceptor(pagination); return interceptor; } }不加这个BeanselectPage执行后其实查的是全表数据不会真正分页。这个坑我见过不少同学踩特意提出来。5.4 密码加密BCrypt别再用MD5账密认证场景下MD5和SHA这类哈希算法是不合适的。它们速度快、无盐值彩虹表一查一个准。正确做法是用BCryptSpring Security里面就带了BCryptPasswordEncoder单独引入spring-security-crypto依赖不用整个引入Spring Security也能用。BCrypt最大的特点是每次加密同一个明文生成的密文都不一样因为它内部自动加盐。验证时matches(明文, 密文)方法判断是否匹配。注册时加密存入登录时匹配判断。实际项目中我会在配置类里注册一个PasswordEncoder的Bean然后用它来做注册和登录的加解密。这个点算是最容易演示的答辩亮点之一——你把密文展示给老师看说明你考虑过真实系统的安全问题比用明文存储的强太多。6. 编码阶段最容易踩的六个坑每个都真实发生在我调试过程中6.1 图片上传后路径失效刷新就404图片保存到本地目录后会遇到一个经典问题数据库存的是D:/upload/car/1.jpg这样的绝对路径结果前后端一部署路径根本访问不了。我的解法是数据库中只存相对路径/upload/car/1.jpg然后在配置类里把本地磁盘目录映射成虚拟路径Configuration public class WebMvcConfig implements WebMvcConfigurer { Value(${carrental.upload-dir}) private String uploadDir; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadDir /); } }这样前端只认/upload/xxx.jpg底层文件放在哪里无所谓。部署到服务器时只用改配置里的路径就行代码不用动。简单有效。6.2 LocalDateTime 前后端显示得一塌糊涂Java 8的LocalDateTime和前端JS的日期格式默认对不上经常出现前端收到一串数字时间戳或者“T”字符分隔的格式。解决起来很简单在配置类里统一注册一个Jackson的序列化器或者直接在实体类时间字段上加JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。我两种方式都用过更推荐在配置类里全局配置这样实体类上不用每个都加注解。6.3 订单状态和车辆状态不同步事务没生效一个典型的“改订单状态成功改车辆状态失败”场景。两个写操作必须在一个事务里要么同成功要么同失败。容易踩的坑是Transactional加在了私有方法上或者同一个类内部调用this.xxx()方法时事务失效。Spring事务是基于代理实现的内部调用代理不生效。正确的打开方式是Transactional加在 public 方法上事务方法由外部调用不要同类内部自调用如果遇到自调用场景把事务方法拆到另一个Service类里或者用AopContext.currentProxy()后者毕设阶段不用深究知道拆类就好。6.4 分页插件配置了还是不生效很多人配好了MybatisPlusInterceptor但还是查出全表常见原因有两个一是版本不兼容。引入的MyBatis-Plus版本太老分页插件类路径变了。3.5.x之后统一用MybatisPlusInterceptor旧的用法已经弃用。二是SQL里自己写了limit或者在XML里没有传递Page参数。分页生效的前提是Mapper方法第一个参数是Page对象或者在XML里用IPage作为参数。不要自己在SQL里写死limit。6.5 跨域拦截器报错OPTIONS请求被卡住前后端分离后页面第一次发请求时会先发一个OPTIONS预检请求。如果拦截器把OPTIONS直接拦了跨域错误层出不穷。处理方式在前面JWT拦截器的代码里有对OPTIONS请求直接放行。另外还需要一个 CORS 全局配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }把这两个点都处理好跨域问题基本就消失了。6.6 Lombok 装上却没生效如果实体类上写了Data但代码里找不到 getter/setter 方法大概率是IDEA的 Lombok 插件没启用或者项目的 Annotation Processing 没打开。结合IDEA的路径Settings → Build, Execution, Deployment → Compiler → Annotation Processors勾选Enable annotation processing。新版IDEA通常会询问是否启用插件老版本需要手动确认。这个问题不大但每次遇到都会让人莫名烦躁。7. 答辩实战把项目讲清楚比把项目写出来更重要7.1 开场三分钟按这个顺序讲不慌很多同学答辩时喜欢从登录页面讲起一页一页点过去结果讲了五分钟老师还不知道你的系统整体是什么样。我的建议是开场按下述节奏来首先用一句话概括系统这是一个基于SpringBoot的汽车租赁管理系统面向管理员和普通用户两类角色实现了从车辆发布、用户下单、订单审核到还车结算的完整租赁闭环。然后快速画一张简单的业务流程图或状态流转图不用太精细目的是让老师建立全局认知。接着讲技术架构后端用SpringBoot持久层用MyBatis-Plus数据库MySQL鉴权用JWT。如果用了Redis或Vue在这时一并带出来。最后演示核心功能时挑三个链路讲不要零散点按钮用户注册登录 → 浏览车辆 → 下单 → 管理员审核 → 还车结算管理员新增车辆 → 上下架用户取消订单或管理员拒绝订单的异常流程按链路演示会让人觉得你对自己的系统有完整把握。7.2 高频追问Top 8提前把答案准备好我把评阅老师最爱问的问题整理成人话版答案你最好能脱稿回答为什么用SpringBoot而不是SSMSpringBoot简化了配置内嵌Tomcat能直接打包运行自动配置帮我们省掉了大量XML配置让开发者更专注业务。这也是当前企业主流的后端框架。SpringBoot自动配置的原理是什么SpringBootApplication包含EnableAutoConfiguration启动时根据META-INF/spring.factories里的配置类结合当前classpath下的依赖按条件装配Bean。JWT和Session有什么差别Session存在服务端内存里分布式场景需要共享存储JWT服务端不存状态每次请求通过验签来认证适合前后端分离和水平扩展。但JWT也有缺点服务端无法主动吊销所以过期时间不能设太长。订单状态是怎么管理的用状态字段加枚举定义了6个状态并按流程转移。状态变更时会加事务保证订单状态和车辆状态一致。并发下同一辆车被两个用户同时下单怎么办我用的是条件更新UPDATE car SET status2 WHERE id? AND status1利用数据库行锁保证只有一个请求能更新成功。数据库有哪些索引为什么建这些用户表的用户名字段建了唯一索引订单表的用户id、车辆id建了普通索引车辆表的品牌建了普通索引。查询驱动的场景下这些索引能有效减少回表次数。分页查询是怎么实现的MyBatis-Plus的分页插件底层是拦截器在SQL执行前把Page参数解析成limit语句自动帮我们拼分页。项目做了哪些测试核心接口用Postman做了完整的功能测试覆盖正常流程和主要异常流程比如重复下单、取消等。如果时间允许可以提一句用Swagger统一了接口文档。这8个问题答好了基本就能稳住了。7.3 演示环节三个容易翻车的细节演示账号一定要提前准备好。管理员和普通用户各准备一个密码设置成简单的提前登录一次避免现场输入错误或者账号被禁用。数据库要提前启动好SQL脚本重新执行一遍保证数据干净。演示的时候在里面故意留一辆可租的车和一个待审核的订单这样流程能一气呵成。如果演示过程中代码报错千万不要当着老师的面改代码。先深吸一口气看错误信息如果是环境问题直接说“这个接口我在单元测试里验证过稍等我看下环境状态”。如果是业务问题诚实说“这里我在开发时可能短路了我的设计预期是XX”也比愣在原地强。最后再分享一个小建议。源码拿到手之后别急着跑起来先用一个下午把三样东西各读两遍建表SQL、订单状态流转和核心Service。能随手画出订单状态图能在黑板上写出订单表字段这比任何润色话术都好用。毕设源码不是什么玄学它就是一套带着业务逻辑的代码你顺着业务走一遍自然就通了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价