☰
SpringBoot+Vue+MyBatis+MySQL共享汽车管理系统开发实战
2026/10/18 18:34:17 网站建设 项目流程

最近把一套共享汽车管理系统从零到一完整搭了一遍,技术栈就是标题里那套:SpringBoot + Vue + MyBatis + MySQL。整个过程走下来,踩了不少坑,也沉淀了不少经验,所以这篇博客打算把这套系统的核心设计思路、关键表结构、接口权限控制、前端页面组织方式、部署启动细节都聊透。如果你正准备做类似的“管理系统”项目,不管是课程设计、毕业设计,还是想接私活练手,这篇文章应该能帮你省下大量试错时间。

先说明一下,我这里不贴完整源码(代码量太大),而是把整个项目的骨架、关键代码片段、表设计 DDL 和配置细节讲清楚,按照这套思路,你完全可以从头复现一个能跑通的共享汽车管理系统。我会尽量把“为什么这么做”也讲明白,不只是甩几张建表语句就完事。

1. 为什么这类项目值得动手做:共享汽车系统背后的真实业务

很多同学一听到“管理系统”四个字,第一反应就是“又是增删改查”,觉得没什么技术含量。但共享汽车管理系统和普通的图书管理、学生管理系统有个本质区别:它有完整的业务闭环和状态流转。从用户注册、实名认证、预约车辆、取车计费、还车结算,到后台的车辆调度、订单审核、异常处理,每个环节都有真实的业务约束。

共享汽车系统的核心参与角色有三类:普通用户、管理员(运营人员)、系统本身。普通用户关心的是“附近有没有车、怎么租、扣多少钱”;管理员关心的是“车辆状态是否正常、订单是否异常、营收怎么统计”;系统则要解决“同一辆车不能被两个人同时预约”“车辆未归还不能再次出租”这类并发和状态一致性问题。这就意味着,你不仅要会写 CRUD,还要懂状态机、懂得如何用数据库约束和事务保证数据正确。

另外,这个项目还有一个很现实的价值:它自带一个相对完整的权限模型。用户端和管理端功能差异很大,接口需要区分角色,这就逼着你用 Spring Security 或拦截器去做认证授权,而这一步恰好是很多增删改查项目里最薄弱、但在面试和答辩里最常被问到的部分。

我的建议是,做这个项目时不要把自己定位成“抄一套代码交差”,而是当成一个真实的产品来打磨。哪怕最终只是课设,只要把业务边界定义清楚、把状态流转理顺、把权限控制做好,这套系统足够支撑你在简历上写一条含金量不错的项目经历。

2. 技术组合的匹配逻辑:SpringBoot + MyBatis + MySQL + Vue 各自扮演什么角色

这套技术栈几乎是国内 Java 全栈开发最经典的组合之一,它流行不是没有原因的。我先拆开讲一下每一层在这个项目里的职责,以及我为什么坚持用这套组合而不是换别的。

2.1 SpringBoot:把“粘合层”的成本降到最低

SpringBoot 在这个项目里的定位,就是提供一套约定优于配置的 Web 框架基础。共享汽车管理系统需要处理 HTTP 请求、参数校验、统一异常、事务管理、定时任务(比如超时订单自动取消),这些能力 SpringBoot 几乎都是开箱即用,不需要像早期 SSM 时代那样写一堆 XML 配置。

我用的版本是 SpringBoot 2.7.x,对应 JDK 1.8。这里有个实际考虑:很多云服务器上的 JDK 环境还是 1.8,踩坑成本最低。如果你本机已经装了 JDK 17 甚至 21,也可以直接用 SpringBoot 3.x,但需要注意 MyBatis 的 starter 命名变化和 jakarta 命名空间的问题,新手很容易被版本坑到。稳定性优先的话,2.7.x 是保守且够用的选择。

2.2 MyBatis:SQL 控得住,性能看得见

共享汽车系统的查询需求非常杂:按地理位置查附近车辆、按时间区间统计订单营收、分页搜索订单、多表关联查用户订单详情。这些场景如果只用 JPA 的自动 CRUD,写起来反而别扭,而 MyBatis 允许你手写 SQL,掌控力最强。我在这套系统里用了 MyBatis 的注解和 XML 混写方式:简单的单表操作直接用注解,复杂关联查询放 XML 里维护。

一个真实感受:MyBatis 的学习曲线比 JPA 平缓很多,而且面试时被问“SQL 优化”相关的问题时,你用 MyBatis 做过的项目可以直接拿出案例来说。比如我在订单查询里就做过一次慢 SQL 优化,加了一个联合索引之后查询时间从 300ms 降到 30ms,这种经验是面试时很加分的。

2.3 MySQL:核心表设计决定业务上限

MySQL 负责把所有业务数据落地,包括用户、车辆、订单、计费规则、操作日志等。这个项目的表结构说多不多,说少不少,大概 10 张左右的核心表就够了。关键不是表的数量,而是表之间的关系和约束。比如订单表和车辆表之间必须有一个状态字段的联动逻辑:车辆被预约后状态从“空闲”改成“已预约”,订单结束之后才能改回“空闲”,这个约束如果用代码实现容易出 bug,但用数据库事务加状态校验就稳很多。

MySQL 的版本,开发环境我用的是 8.0,因为 8.0 的窗口函数和通用表表达式在做统计报表时非常好用。如果你还在用 5.7 也没关系,这个项目的核心功能不依赖 8.0 的特有语法。

2.4 Vue:管理端和用户端的分裂开发策略

前端部分我选了 Vue 2 + Element UI,不是因为 Vue 3 不好,而是 Element UI 的生态对“管理系统”类型的页面支持非常成熟:表格、表单、弹窗、分页、日期选择器,全部现成,改改就能用。如果你坚持用 Vue 3,也可以搭配 Element Plus,语法上需要适应一下setup语法糖。

这个项目的前端我分成了两个独立页面入口:用户端是面向普通用户的操作界面(找车、预约、支付、查看订单),管理端是后台运营界面(车辆审核、订单管理、用户管理、数据统计)。两个页面共用一套 API 封装和路由配置,但页面结构差异很大。这种分裂式开发的好处是职责清晰,缺点是工作量大一些,所以我建议先做管理端再做用户端,因为管理端覆盖了绝大部分核心数据操作逻辑。

2.5 版本选型清单

给出一份可以直接照着装的版本组合,降低配置风险:

组件推荐版本说明
JDK1.8(或 11)最稳定,兼容所有依赖
SpringBoot2.7.x使用javax命名空间,教程最多
MyBatismybatis-spring-boot-starter 2.3.x官方 starter
MySQL8.0.x支持窗口函数
Vue2.6.x配合 Element UI 2.15.x
Node14.x / 16.x太高版本可能编译报错
Maven3.6.x / 3.8.x常规版本即可

3. 数据库设计:共享汽车系统最核心的几张表长什么样

表设计是这种管理系统项目的灵魂。代码写得再漂亮,如果表结构不合理,后期改起来就是灾难。我直接把我整理好的核心表结构和设计思路分享出来,带注释和关键索引,你照着建就能用。

3.1 用户表(user)

用户表不算复杂,但要考虑扩展。共享汽车系统的用户信息至少要包含账号、密码、手机号、身份证号(实名认证需要)、驾驶证号、用户状态、余额等字段。这里有两个设计细节:

  • 密码不需要明文存储,用 MD5 加盐或者 BCrypt 哈希。我在项目里用的是 BCrypt,Spring Security 自带的加密器,安全度够且不需要自己写加盐逻辑。
  • 余额字段建议用DECIMAL(10,2)而不是FLOAT,避免浮点精度导致计费误差,这是计费系统的基本素养。
CREATE TABLE `user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL COMMENT '登录账号', `password` varchar(100) NOT NULL COMMENT 'BCrypt加密后的密码', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `id_card` varchar(20) DEFAULT NULL COMMENT '身份证号', `driver_no` varchar(30) DEFAULT NULL COMMENT '驾驶证号', `balance` decimal(10,2) DEFAULT '0.00' COMMENT '账户余额', `status` tinyint DEFAULT '1' COMMENT '1正常 0禁用', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`), KEY `idx_phone` (`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

3.2 车辆表(car)

车辆表是共享汽车系统的核心资源表。字段上除了基本的车牌号、品牌、型号、颜色,还要有车辆状态、所在网点(或停车场)、每公里价格、每日租金上限、车辆图片等。最重要的是status字段,它的状态流转直接决定了业务能不能继续:

  • 0:维护中(后台禁用)
  • 1:空闲(可预约)
  • 2:已预约(等待用户取车)
  • 3:使用中(用户已取车)
  • 4:异常(上报故障,需人工处理)
CREATE TABLE `car` ( `id` int NOT NULL AUTO_INCREMENT, `plate_no` varchar(20) NOT NULL COMMENT '车牌号', `brand` varchar(30) DEFAULT NULL COMMENT '品牌', `model` varchar(30) DEFAULT NULL COMMENT '车型', `color` varchar(20) DEFAULT NULL, `status` tinyint DEFAULT '1' COMMENT '0维护 1空闲 2已预约 3使用中 4异常', `station_id` int DEFAULT NULL COMMENT '所在网点id', `price_per_hour` decimal(10,2) DEFAULT '30.00' COMMENT '每小时单价', `price_per_day` decimal(10,2) DEFAULT '200.00' COMMENT '每日封顶价', `car_img` varchar(255) DEFAULT NULL COMMENT '车辆图片URL', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_plate` (`plate_no`), KEY `idx_status_station` (`status`, `station_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='共享汽车表';

这里我建了一个联合索引(status, station_id),因为实际查询里最常见的操作是“查询某个网点下所有空闲车辆”,两个字段一起过滤,索引能直接命中。

3.3 订单表(order_info)

订单表是整个系统最复杂的一张表,它把用户、车辆、时间、价格、状态全部串起来。字段设计上核心是:订单号、用户 ID、车辆 ID、取车网点、还车网点、预计取车时间、实际上车时间、实际还车时间、订单状态、总金额、支付状态、创建时间。

订单状态我定义了这样几个值,正好对应业务流转的每个步骤:

  • 1:待取车(用户已下单,还没到车上)
  • 2:使用中(已扫码或取车)
  • 3:已完成(还车且结算完成)
  • 4:已取消(用户主动取消)
  • 5:超时取消(系统定时任务自动取消)
CREATE TABLE `order_info` ( `id` int NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '业务订单号', `user_id` int NOT NULL, `car_id` int NOT NULL, `pickup_station_id` int DEFAULT NULL COMMENT '取车网点', `return_station_id` int DEFAULT NULL COMMENT '还车网点', `pickup_time` datetime DEFAULT NULL COMMENT '实际上车时间', `return_time` datetime DEFAULT NULL COMMENT '实际还车时间', `expect_pickup_time` datetime DEFAULT NULL COMMENT '预计用车时间', `status` tinyint DEFAULT '1' COMMENT '1待取车 2使用中 3已完成 4已取消 5超时取消', `total_amount` decimal(10,2) DEFAULT '0.00' COMMENT '订单总金额', `pay_status` tinyint DEFAULT '0' COMMENT '0未支付 1已支付', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_user_id` (`user_id`), KEY `idx_car_status` (`car_id`, `status`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

order_no我单独设置了唯一索引,而不是直接用自增 ID 当订单号,因为业务上订单号通常要展示给用户,比如 APP 订单详情页,自增 ID 暴露出去既不安全也不好看。我在生成订单号时用的是“时间戳 + 随机数”的组合,格式类似20250101123000123456。

3.4 计费规则表(fee_rule)

计费规则我单独拆了一张表,而不是写死在代码里。这样做的好处是运营人员可以后台改收费规则而不需要重新发版。字段包括:计费类型、基础价格、单位时长、每日封顶价、超时单价。

CREATE TABLE `fee_rule` ( `id` int NOT NULL AUTO_INCREMENT, `rule_name` varchar(50) DEFAULT NULL COMMENT '规则名称,如'标准计费'', `base_price` decimal(10,2) DEFAULT '0.00' COMMENT '起步价', `hourly_price` decimal(10,2) DEFAULT '0.00' COMMENT '每小时价格', `daily_cap` decimal(10,2) DEFAULT '0.00' COMMENT '24小时封顶', `delay_price` decimal(10,2) DEFAULT '0.00' COMMENT '超时每小时费用', `status` tinyint DEFAULT '1' COMMENT '1启用 0停用', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='计费规则表';

实际结算的逻辑我写在 service 层:先比较用车时长和封顶值,如果用车时长超过一天的部分,按小时单价计算,下一个自然日重新计算封顶。举个例子,用户用车 30 小时,那么前 24 小时按日封顶价计算,后 6 小时按小时单价计算。这个逻辑用代码实现很简单,但要注意边界条件。

3.5 其他辅助表

除了上面四张核心表,还需要几张辅助表来支撑功能完整性:

  • 网点表(station):记录停车点名称、位置、可停放车辆数、经纬度,方便后续做地图找车功能。
  • 操作日志表(operation_log):记录关键敏感操作,比如用户禁用、车辆上下架、订单强制取消。这张表很推荐加,答辩或真实运营时能派上大用场。
  • 公告表(notice):后台发布系统公告,用户端轮播展示。

这些表的设计逻辑比较简单,我不再一一贴 DDL。核心思想就一句话:表的数量不贪多,但每张表都要有存在的业务理由。

4. 后端接口与权限控制:从登录鉴权到订单流转的完整链路

表结构设计完之后,就要开始写后端接口了。我把这个过程拆成三条主线讲:登录鉴权怎么做、车辆与订单的接口怎么设计、事务和并发怎么控制。

4.1 登录鉴权:JWT + 拦截器,轻量但够用

直接引入 Spring Security 再配一堆过滤器链,对新手来说太重了。这个项目我选择了一个更轻量的方案:JWT + HandlerInterceptor 拦截器。逻辑是这样的:

  • 用户登录成功后,后端生成一个 JWT token,过期时间设置为 24 小时,token 里带上用户 ID、用户名、角色编码。
  • 前端把 token 存在 localStorage 里,之后每次请求在 Header 里带Authorization: Bearer <token>。
  • 后端写一个拦截器,在 preHandle 里解析 token,解析成功就把用户信息存入 ThreadLocal,方便后续业务代码直接获取当前登录用户;失败则返回 401。

核心拦截器代码大概长这样,简单但有代表性:

public class AuthInterceptor 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 (StringUtils.hasText(token) && token.startsWith("Bearer ")) { token = token.substring(7); } try { Claims claims = JwtUtil.parseToken(token); UserContext.setUserId(Long.parseLong(claims.get("userId").toString())); UserContext.setRole(claims.get("role").toString()); return true; } catch (Exception e) { response.setStatus(401); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":401,\"msg\":\"登录已过期,请重新登录\"}"); return false; } } }

在 WxMpService(其实这里应该是 WebMvcConfigurer)里注册拦截器,并配置放行路径。登录接口、注册接口、车辆查询接口这些不需要登录就能访问的路径要放行,而订单相关、用户信息相关接口则全部拦截。

4.2 车辆查询与预约下单:核心接口的设计逻辑

车辆查询接口是用户端用得最频繁的接口,核心是支持按条件分页搜索车辆,条件包括:网点 ID、车辆状态、品牌、价格范围。Service 层直接复用 MyBatis 动态 SQL,传入一个查询条件对象,返回带分页信息的车辆列表。这里要注意,返回给前端的必须是车辆信息的 VO 对象,绝对不能把数据库中所有字段直接透出,尤其是内部备注字段。

预约下单是这个系统里最容易出并发问题的一步。用户点了预约,系统要做什么?不是直接把车辆状态改成“已预约”就完事,而是要做三件事,并且这三件事必须在同一个事务里:

  1. 校验车辆状态必须是“空闲”,如果状态不是空闲,直接抛出业务异常。
  2. 插入一条状态为“待取车”的订单记录。
  3. 把车辆状态从“空闲”更新为“已预约”。

关键逻辑在第 3 步的更新语句上。防止并发下两单的秘诀是,使用“乐观更新”的方式执行 SQL:

UPDATE car SET status = 2 WHERE id = #{carId} AND status = 1

这条 SQL 的影响行数要么是 1,要么是 0。如果两个人同时预约同一辆车,数据库层面的行锁会保证只有一个人的 UPDATE 影响行数为 1,另一个人拿到影响行数为 0 后,代码里判断为“车辆已被他人预约”,回滚事务并抛出提示。这种方式比先 SELECT 再 UPDATE 的方案更可靠,也是单机部署下最简单有效的防并发手段。

4.3 还车结算:订单状态流转和金额计算

还车结算的逻辑稍复杂一些。用户在小程序或网页上点击“立即还车”后,后端要做这几步:

  1. 根据订单号查出订单,校验订单状态必须是“使用中”。
  2. 计算出用车时长,通常精确到分钟,不足一小时按一小时计算(这个规则可以配置)。
  3. 调用计费规则,计算出总费用。
  4. 更新订单状态为“已完成”,设置实际上车时间、实际还车时间、总金额、支付状态。
  5. 把车辆状态更新为“空闲”,并可能更新车辆当前所在网点。

这里还有一个额外逻辑,就是扣费。我不建议在还车接口里同步扣费,因为涉及余额变更和流水记录,最好把“还车 + 算价 + 更新状态”整合在一个事务里,扣费单独做一个接口由前端在展示结算页后调用。当然,也可以把扣费做成一个独立事务的消息,但对于这个体量的系统,直接在还车接口的同一个事务里执行余额变更也够用。

4.4 后台管理接口:数据列表和状态修改

管理员端接口相对直接,但有一个设计原则值得说:管理接口必须走单独的前缀,比如/api/admin/**,在拦截器里做双重校验,不仅要求登录,还要校验角色必须是管理员。任何管理员操作(比如禁用用户、下架车辆、强制取消订单),都要记录操作日志。这是我踩过坑后补上的:某次测试时发现后台数据被误改,但因为没日志,完全不知道是哪一步操作导致的,查了很久。后来加了个简单的操作日志切面,通过 AOP 拦截带特定注解的方法,自动记录用户 ID、操作方法、参数和时间。

5. 前端 Vue 页面的组织方式:哪些页面绕不开,哪些细节容易翻车

前端这块我重点讲页面拆分和几个高频率踩坑点,不会贴所有的组件代码。

5.1 页面结构与路由设计

用户端和管理端我建议在同一个 Vue 工程里用路由区分,而不是拆成两个独立工程。这样做的好处是能复用封装好的 axios 实例、工具函数和组件,工程结构也简单。

路由设计大致如下:

  • /login:登录页(用户和管理员共用,根据不同角色跳转不同首页)
  • /user/**:用户端页面,包括首页车辆列表、车辆详情、订单列表、个人中心
  • /admin/**:管理端页面,包括工作台、车辆管理、订单管理、用户管理、计费规则管理、公告管理

路由守卫必须加,原因很简单:用户直接输入/admin/carList就能看到管理页面?前端路由守卫只能在页面级别做控制,真正的安全防线还是后端拦截器。前端路由守卫的作用主要是拦截未登录用户跳转到登录页,代码逻辑大概是:读取 localStorage 里的 token,如果没有或者过期,直接router.push('/login')。

5.2 车辆列表页和预约流程

车辆列表页是用户端的门面。真实项目中一般会有地图找车,但那个逻辑比较复杂,建议第一版先做列表模式。列表页展示车辆图片、品牌、价格、位置、状态,并提供按网点和品牌筛选。

预约流程要处理好状态回显。用户点击“预约”按钮后,前端应该立即把按钮置灰、显示“预约中”,等后端返回成功后再跳转到订单详情页。如果后端返回“车辆已被预约”,要给出明确提示。这个交互细节很多人会忽略,但真实用户使用时体验差异很大。

5.3 管理端的表格页设计

管理端的车辆管理、订单管理、用户管理本质上都是“表格 + 搜索 + 分页 + 操作列”的组合,Element UI 的el-table基本都能搞定。我的建议是做一个通用的分页查询组件,把“搜索表单 + 表格 + 分页器”封装起来,后续页面只需要配置列和请求接口就能复用,能省下大量重复代码。

分页封装时,要注意把分页参数(currentPage、pageSize)和搜索条件分离,搜索条件变化时页码要重置回第一页。这个细节很多人会漏掉,导致搜索时发现数据对不上,其实是当前页码超过了搜索结果的总页数。

5.4 前端容易翻车的几个坑

前端看着简单,实际跑起来坑不少。我列几个我踩过的,都挺典型:

  • axios 拦截器里对 401 的处理:如果 token 过期,后端返回 401,前端需要清除本地 token 并跳转登录页,而不是单纯报错。很多项目没做这步,导致用户停留在页面里反复请求失败。
  • 日期格式化:Element UI 的el-date-picker返回的时间默认是 UTC 字符串,不转换直接传给后端会导致时间差 8 小时。我在工具函数里统一写了formatDate,提交前强制转成yyyy-MM-dd HH:mm:ss格式。
  • 接口地址的跨域配置:开发环境下通过 Vue 的 devServer 配置代理转发请求,避免前端请求直接打在后端端口上。生产环境则要用 Nginx 反向代理,把/api路径转发到后端服务。代理配置是最容易被忽略的,很多人开发环境跑通后部署到服务器就跨域了,就是因为少了这一步。
// vue.config.js 开发环境代理配置 module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

6. 项目启动与部署的实测记录:从本地到服务器的完整流程

项目的代码和数据库都准备好之后,剩下的就是把整套系统跑起来。这部分我记录了实际的启动流程和部署过程中遇到的几个报错。

6.1 本地启动步骤

本地启动的顺序有讲究,我建议按这样的顺序来:

  1. 先在 MySQL 里创建数据库,执行建表 SQL,并导入一份测试数据(车辆、网点、管理员账号)。
  2. 启动后端项目:修改application.yml里的数据库连接、Redis 连接(如果用了)和 JWT 密钥,直接运行 main 类。
  3. 启动前端项目:npm install安装依赖,然后npm run serve启动开发服务器。
  4. 浏览器访问前端地址,先拿管理员账号登录后端点一遍功能,确认后端接口正常。

这里有一个很容易忽略的点:如果直接打开前端页面,不经过代理,浏览器访问的是前端开发服务器地址(默认 8081 或 8080),而后端接口在另一个端口(默认 8080),请求会跨域。所以一定要确认 devServer 代理配置生效,否则第一个请求就会因为 CORS 报错。前端访问的接口路径应该统一是/api/xxx,由代理转发到后端。

6.2 生产环境部署

生产环境我采用前后端分离部署的思路:

  • 前端:执行npm run build,构建结果是 dist 目录下的静态文件,把这些文件放到 Nginx 的 html 目录里。
  • 后端:执行mvn clean package -DskipTests,打出一个可执行 jar 包,通过java -jar xxx.jar启动。

Nginx 配置是关键。需要做的事情有两件:一是把前端静态文件路径指到 dist 目录;二是把/api开头的请求反向代理到后端的 8080 端口。如果前端使用了 history 路由模式,还需要配置try_files $uri /index.html;,否则刷新页面会出现 404。这个 404 坑太常见了,一定要写进 Nginx 配置里。

server { listen 80; server_name your_domain; location / { root /usr/share/nginx/html/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; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }

6.3 部署中遇到的三个典型问题

我这次部署到云服务器(Linux 环境)时,遇到了三个比较典型的报错,直接列出来供参考:

第一个是“数据库连接拒绝”。原因是 MySQL 默认只监听本地 127.0.0.1,如果后端 jar 包也在同一台机器上,这个没问题;但如果后端和数据库分离部署,或者用 Navicat 远程连库,就会拒绝访问。解决办法是修改 MySQL 的账户授权,允许指定 IP 访问,或者调整 bind-address 配置。生产环境更安全做法是通过防火墙限制访问来源。

第二个是“端口被占用”。后端默认 8080 端口经常会被其他 Java 进程占用,启动时报Address already in use。解决方式是先netstat -tlnp | grep 8080看看占用进程,确认后可以改后端端口,或者直接pkill -f java把旧进程停掉。

第三个是“前端刷新 404”。前面已经说过,history 路由模式下刷新页面,Nginx 没有try_files配置就会报这个错。加上那一行就能解决。

6.4 初始化数据准备

登录入口默认是管理员账号,我在初始化 SQL 里写了一个固定的账号密码,比如账号admin,密码是 BCrypt 加密后的123456。用户端则需要通过注册接口自助创建。测试时我最常干的事是:注册两个用户账号、添加三辆测试车辆、建两个网点,然后模拟一整套流程——预约、取车、还车、结算,核对数据是否正确。整个流程跑通,这个项目的主体功能就算验证完成了。

7. 把课设项目变成可落地的商业原型:扩展思路与实战建议

许多同学做完这个系统就交差,其实有点可惜。只要在现有基础之上做一些关键改进,这个项目的价值能提升一个层级。我给几个我认为性价比最高的扩展方向,供你参考。

7.1 消息通知与异常状态处理

真实共享汽车业务里,“超时未取车自动取消订单”是非常常见的运营手段。实现方式是在 SpringBoot 里加一个定时任务,每隔一段时间扫描一次状态为“待取车”且预期取车时间已过 15 分钟的订单,自动把订单状态改为“超时取消”,并把车辆状态恢复为“空闲”。代码实现不复杂,但有了这个逻辑,整个系统的闭环就更完整了。如果再进一步,可以对接短信或小程序模板消息,在车辆状态异常、订单即将超时等场景自动通知用户,这个能力在答辩时是很亮眼的加分项。

7.2 地图找车:让车辆分布可视化

共享汽车系统的高级感通常来自地图找车功能。后端方面需要给网点表加上经纬度字段,提供一个按经纬度和半径查询附近可用车辆的接口;前端方面可以集成高德地图或百度地图的 JavaScript API,把车辆以标记点形式渲染在地图上。这个功能不难,但需要申请地图开发者 key,对接时注意坐标系转换问题(高德是 GCJ-02,百度是 BD-09,需要换算)。第一版建议先用“网点列表 + 车辆列表”的形式,地图找车可以作为二期迭代方向。

7.3 支付对接

如果想让系统更接近真实产品,支付模块必不可少。接入支付宝或微信支付有一定的复杂度:需要商户号、证书、回调接口等。课程设计阶段不一定能搞定真实支付渠道,但可以先做一个模拟支付:用户点击“确认支付”后,调用一个 mock 接口直接扣减用户余额并更新支付状态,同时生成支付流水。这样既避免了真实支付申请的繁琐,也保留了完整的业务链路。面试时可以自信地说“支付模块支持真实渠道切换,当前默认走余额模拟支付”。

7.4 运维与监控

最后提一个很多人不重视但实际很实用的点:加上一套简单的监控与告警机制。可以在后端加一个 Actuator 健康检查接口,配合一个定时脚本,检测到服务挂了就自动重启;数据库层面的慢查询日志也可以打开,不定期刷一下看看有没有慢 SQL。这些运维习惯不会直接体现在功能清单里,但它体现了你对一个系统整体生命周期的理解,这种综合素质在真实工作环境中非常关键。

说实话,把这个共享汽车管理系统完整跑下来,我对“管理系统”这四个字的理解有了变化。它的难点从来不在某个技术点上,而在于如何把多个技术栈拼成一个链路完整、状态正确、权限清晰、可以实现业务闭环的系统。SpringBoot 负责把后端的各种能力组织起来,MyBatis 让你对 SQL 有完全的掌控力,MySQL 用表和索引承载业务约束,Vue 把操作界面变得直观好用。把这套组合吃透,你不仅拥有一个能展示的项目,更重要的是建立了一套“从业务需求到系统实现”的拆解能力。这套能力,是任何一个具体框架更新换代都带不走的底子。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询