恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
SpringBoot+Vue公交线路查询系统设计与实现详解
首页
资讯中心
/
SpringBoot+Vue公交线路查询系统设计与实现详解
SpringBoot+Vue公交线路查询系统设计与实现详解
发布时间:2026/10/12 6:39:08
1. 项目整体设计与技术选型1.1 为什么选SpringBootVue做公交查询系统说到SpringBootVue的公交线路查询系统管理平台很多准备毕业设计或者课程设计的同学应该都不陌生。说句实话公交线路查询这个题目既不像电商系统那样涉及大量支付和库存逻辑也不像纯后台管理系统那样缺少业务亮点它最舒服的地方在于“数据关系清晰、业务闭环完整、演示效果直观”用来做毕设/课设再合适不过。这个项目的技术组合是SpringBootVueJavaMySQL前后端完全分离。后端SpringBoot负责提供RESTful API前端Vue负责页面渲染和用户交互MySQL负责存储所有公交线路、站点、用户等数据。作为学习项目它几乎把企业开发的主流技术栈都过了一遍Maven依赖管理、MyBatis持久层、RESTful接口设计、Vue组件化开发、前后端联调、数据库设计每一项都是将来工作会直接用到的东西。我最推荐这个项目的原因还在于它的复杂度刚刚好。线路和站点之间是多对多关系换乘查询有一定算法逻辑后台管理又涉及CRUD和权限控制该有的都有了但又不至于像中台系统那样复杂到啃不动。哪怕你是Java基础一般的同学花一到两周把这个系统完整做一遍对SpringBoot和Vue的整体认知会直接上一个台阶。1.2 功能模块拆解从需求到落地做项目最怕一上来就写代码先把功能拆清楚比什么都重要。公交线路查询系统管理平台大致可以分成用户端和管理端两条主线。用户端的核心是“查线路”输入起点站名或者线路名称系统返回匹配的公交线路信息包含线路编号、首末班时间、票价、全程站数、经过哪些站点等。如果用户输入两个站点还能提供直达或换乘建议。另外用户可以查看站点详情比如这个站点有哪几路公交车经过方便在实际出行场景中使用。管理端的核心是“管数据”线路管理、站点管理、线路站点顺序维护、用户反馈处理。线路管理包括新增公交线路、修改线路信息、上下线停运等站点管理负责维护站点的名称和位置线路站点顺序维护是最容易忽略的环节因为一条线路要经过哪些站点、顺序如何、从哪一站开始哪一站结束这些数据一定要用关联表来维护否则查询时全乱套。从流程上来说数据由管理员录入存入MySQL用户通过前端界面发起请求后端负责从数据库取数再返回给前端渲染。整个系统的业务闭环就是“数据录入—数据入库—数据查询—结果展示”这也是为什么这类项目特别适合做课程设计因为每个环节都能讲清楚。1.3 技术选型的深层原因很多同学在答辩时会被问到为什么不用SSM为什么不用JSP这里我建议你提前把这些问题想透。SpringBoot之所以取代了传统SSMSpringSpringMVCMyBatis核心在于零配置和自动装配。以前搭一个SSM项目要写一堆XML配置文件光是Spring和MyBatis的整合就能折腾一整天。SpringBoot通过注解和自动配置把大部分样板配置都干掉了项目结构更清晰开发者只需要关心业务逻辑。对于毕业设计来说用SpringBoot意味着你不需要在环境配置上花太多时间而是可以把精力放在代码本身。Vue这边同理。以前做JavaWeb用的都是JSP服务端渲染前后端不分离改个样式都要重启服务器非常痛苦。Vue作为前端框架核心是组件化和响应式数据绑定。写页面的时候把重复的部分拆成组件数据一变页面自动更新交互体验比传统页面好太多。而且Vue的学习曲线相对平缓官方文档也齐全适合没有前端基础的同学上手。至于MySQL它开源免费、轻量可靠在中小型项目里几乎是无脑选择。配合Navicat或者命令行建库建表都很方便。MySQL的索引机制和事务特性在这个项目里虽然用得不多但足以支撑学校级别的业务场景。如果非要说有什么要注意的那就是MySQL 5.7和8.0在密码加密和排序规则上有些差别项目里最好统一版本避免后面排查半天。2. 数据库设计与核心表结构2.1 数据模型设计思路公交线路查询系统最核心的数据关系就是线路和站点的多对多关系。一条线路经过很多站点一个站点也可能有多条线路经过。如果直接在线路表里存一个“站点列表”字段查询某个站点有哪些线路时就会非常痛苦。所以数据库设计一定要遵守范式拆出一张关联表来维护线路和站点的关系。我当时的做法是设计了四张核心业务表station站点表、bus_line公交线路表、line_station线路站点关联表、user用户表另外加了一张feedback投诉建议表用来丰富系统功能。扩展性上如果以后要做实时定位还可以加vehicle和location_record但作为毕设这五张表完全够了。line_station这张关联表是整个系统的灵魂。它里面不仅要存line_id和station_id还要有一个station_order字段来表示这个站点在线路上的第几站。没有了这个排序字段你根本没法还原一条线路到底先经过谁再经过谁。这点在演示时一定要重点讲因为这是数据库设计里体现水平的地方。2.2 核心表结构详细说明下面给出我实际使用的建表SQL字段类型和注释都做了精简你可以直接参考。-- 站点表 CREATE TABLE station ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 站点名称, address varchar(200) DEFAULT NULL COMMENT 站点地址, longitude decimal(10,6) DEFAULT NULL COMMENT 经度, latitude decimal(10,6) DEFAULT NULL COMMENT 纬度, created_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_name (name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT公交站点表;-- 公交线路表 CREATE TABLE bus_line ( id int(11) NOT NULL AUTO_INCREMENT, line_name varchar(50) NOT NULL COMMENT 线路名称如1路, line_type tinyint(4) DEFAULT 1 COMMENT 线路类型1普通线路2快速公交3夜班线, start_station varchar(50) DEFAULT NULL COMMENT 起点站名称, end_station varchar(50) DEFAULT NULL COMMENT 终点站名称, start_time varchar(10) DEFAULT NULL COMMENT 首班时间 HH:mm, end_time varchar(10) DEFAULT NULL COMMENT 末班时间 HH:mm, price decimal(4,2) DEFAULT 0.00 COMMENT 票价, mileage decimal(6,2) DEFAULT NULL COMMENT 里程公里, status tinyint(4) DEFAULT 1 COMMENT 1运营0停运, created_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_line_name (line_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT公交线路表;-- 线路站点关联表 CREATE TABLE line_station ( id int(11) NOT NULL AUTO_INCREMENT, line_id int(11) NOT NULL, station_id int(11) NOT NULL, station_order int(11) NOT NULL DEFAULT 0 COMMENT 站点顺序从1开始, PRIMARY KEY (id), UNIQUE KEY uk_line_station (line_id,station_id), KEY idx_station (station_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT线路站点关联表;-- 用户表 CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(100) NOT NULL COMMENT BCrypt加密后的密码, nickname varchar(50) DEFAULT NULL, role tinyint(4) DEFAULT 1 COMMENT 1普通用户2管理员, status tinyint(4) DEFAULT 1 COMMENT 1正常0禁用, created_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;说明几个字段设计的考量。站点表里存经纬度虽然这个项目可能不会真正对接地图但有了经纬度后面做地图展示时就不用再改表结构了。线路表里的line_type字段很重要它能让你的查询支持按线路类型过滤这就是一个很好的答辩加分点。关联表里的station_order字段我特意强调一下这里不能存站点id的拼接字符串不要图省事否则后面查换乘方案时会写出极其恶心的SQL。2.3 初始数据脚本准备建完表还不行得先塞点数据跑起来。我的做法是每张表插20条左右的记录。线路可以编造一些类似于“1路、2路、5路、K01路”这样的名称站点就是“火车站、人民广场、市政府、中心医院、汽车南站”这类城市里耳熟能详的名字。关键是关联表的数据要能形成“交叉网”也就是说至少要保证几个站点同时出现在多条线路上这样换乘查询才有数据可以演示。插入数据时有一个小坑如果你用的是Navicat的导入功能要注意字符集选utf8mb4否则中文很容易变成问号。另外station_order字段必须从1开始连续递增不能出现0或者跳数字否则你还原完整线路时候选会出现一个站点一闪而过的情况。我的建议是在初始化脚本里用INSERT INTO line_station(line_id, station_id, station_order) VALUES (1,1,1),(1,2,2)...这样的显式声明清晰也好排查。3. 后端接口开发实战3.1 SpringBoot项目初始化与分层结构创建SpringBoot项目我习惯用Spring InitializrIDEA和网页版都行。选择Java 8或11打包方式选jar依赖里面勾选Spring Web、MyBatis Framework、MySQL Driver、Lombok。Lombok一定要加能省掉一大堆getter/setter的重复代码看着清爽答辩时也显得你懂开发效率。项目结构建议这样分包com.example.transit ├── controller ├── service │ └── impl ├── mapper ├── entity ├── common │ ├── result │ ├── exception │ └── config └── TransitApplication.javacontroller只负责接收请求和返回响应不写业务逻辑。service层写真正的业务逻辑包括换乘算法、事务控制等。mapper层是MyBatis的接口配合XML文件或者注解写SQL。entity对应数据库的表结构。common里放统一返回结果、全局异常处理器、配置类。这种分层结构不是花架子。答辩的时候老师一定会问“你项目的分层结构是怎样的”照这个标准结构去回答能体现出你有工程化思维不是只会写CRUD。而且分层之后代码的维护性也好了很多比如你之后想把查询逻辑从内存换成Redis缓存只需要改service层不用动controller和前端。3.2 线路查询接口的核心业务逻辑这个项目的核心接口有两个按线路名或站点名模糊查询线路列表根据起点站和终点站查询直达或换乘方案。先写一个最基础的模糊查询接口。比如用户输入“人民广场”需要查出所有经过“人民广场”的线路。这个时候我们就不能只查bus_line表而是要联表查询。MyBatis的写法可以这样select idsearchLinesByStationName resultTypecom.example.transit.entity.BusLine SELECT DISTINCT bl.* FROM bus_line bl INNER JOIN line_station ls ON bl.id ls.line_id INNER JOIN station st ON ls.station_id st.id WHERE st.name LIKE CONCAT(%, #{keyword}, %) /select这里用DISTINCT是为了防止一条线路多次经过同一个站点时出现重复数据。我踩过的坑就是没有加DISTINCT结果用户查“火车站”时返回了两条完全相同的1路线看起来特别业余。然后是换乘查询算法。最朴素的方案是“两段式查询”先查所有经过起点站的线路集合再查所有经过终点站的线路集合然后找两个集合的交集。如果交集不为空说明有直达线路。如果没有交集就枚举每一对线路找出它们共同经过的站点作为换乘点这就是一次换乘方案。伪代码思路// 1. 找到经过起点站的所有线路 ListBusLine startLines lineStationMapper.selectLinesByStationId(startStationId); // 2. 找到经过终点站的所有线路 ListBusLine endLines lineStationMapper.selectLinesByStationId(endStationId); // 3. 判断有没有交集有就返回直达 ListBusLine directLines startLines.stream() .filter(endLines::contains) .collect(Collectors.toList()); // 4. 如果没有直达尝试一次换乘 for (BusLine sLine : startLines) { for (BusLine eLine : endLines) { ListStation sStations getStationsByLine(sLine.getId()); ListStation eStations getStationsByLine(eLine.getId()); // 取交集即为换乘站 } }这个算法的时间复杂度不高因为数据量小完全够用。如果你想优化可以预先加一个站点到线路集合的Map把重复查询改成内存判断。但作为毕设先把逻辑讲清楚比闷头造高性能方案更重要。3.3 管理后台CRUD与权限控制管理后台部分核心是线路和站点的增删改查。线路的删除要特别小心因为bus_line和line_station存在外键关联直接删除线路会导致关联表残留脏数据。我的做法是删除线路时开启事务先删掉line_station里所有该线路的记录再删bus_line里的线路记录。SpringBoot的Transactional注解就能搞定Transactional public void deleteLine(Integer lineId) { lineStationMapper.deleteByLineId(lineId); busLineMapper.deleteById(lineId); }如果漏了Transactional删除到一半报错会导致数据不一致到时候整张关联表全是孤立的记录查线路时会出现线路ID找不到对应线路的错误非常难排查。权限控制这块我用的是比较轻量的拦截器方案。用户登录成功后返回一个JWT token前端在请求头里带上Authorization: token后端写一个LoginInterceptor去校验token和用户角色。只有角色为管理员role2的请求才允许访问/admin/**路径。这个方案比Spring Security简单得多而且能学到权限控制的核心思想非常适合毕设。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !JwtUtil.verifyToken(token)) { response.setStatus(401); return false; } return true; } }3.4 接口清单与统一返回格式为了方便联调和写文档我建议把接口设计成统一风格。下面是我这个项目的接口清单用表格列出来一目了然方法路径功能参数GET/api/line/search按关键字搜索线路keywordGET/api/line/detail/{id}查看线路完整站点信息idGET/api/transfer查询换乘方案startStation, endStationGET/api/station/list站点列表namePOST/api/admin/line新增线路线路JSONPUT/api/admin/line/{id}修改线路线路JSONDELETE/api/admin/line/{id}删除线路idPOST/api/admin/line/station设置线路站点顺序lineId, stationId, orderPOST/api/user/login登录username, passwordPOST/api/user/feedback提交反馈content统一返回格式我定义了ResultT类包含code、message、data三个字段。成功时code200失败时code非200配合全局异常处理器前端可以统一判断接口是否成功。这样做的好处是前端不需要对每个接口单独做错误处理一个拦截器就能搞定。4. 前端Vue页面开发4.1 Vue项目搭建与路由设计前端我用的是Vue CLI脚手架。命令简单但要注意Node版本Vue CLI 5需要Node 12以上推荐Node 14 LTS。项目创建好之后先装核心依赖npm install axios element-ui vue-router3 vuex3Vue 2 Element UI是这类毕设项目里出现频率最高的组合因为Element UI对Vue 2支持最完善组件风格也专业。如果你用的是Vue 3那得换Element PlusAPI有些区别最好一开始就想清楚用哪个别中途切换。路由设计上我把页面划分成用户端和管理端两块。用户端包括首页/、线路查询/search、线路详情/line/:id、站点列表/station管理端包括登录/login、后台首页/admin、线路管理/admin/line、站点管理/admin/station。管理端统一挂在/admin路由下然后在router.beforeEach里加路由守卫检查localStorage里的token没有token就跳去登录页。4.2 线路查询页面从输入到实时展示查询页面的核心是一个搜索框加一个结果列表搜索框绑定一个keyword数据点击“查询”按钮时调用axios方法。这里我第一次写的时候没有做防抖用户敲一个字就发一次请求不仅容易被后端限流体验也差。后来加了个最简单的keyup监听加500毫秒延时或者直接用keyup.enter触发效果好很多。查询接口调用代码async function searchLines() { loading.value true try { const res await http.get(/api/line/search, { params: { keyword: keyword.value } }) lineList.value res.data.data } catch (e) { ElMessage.error(查询失败请重试) } finally { loading.value false } }返回的每条线路信息用el-card展示卡片上写线路名、起终点、首末班时间、票价点击卡片可以跳转到线路详情页。详情页再通过lineId请求/api/line/detail/{id}拿到完整的站点列表用el-steps组件按顺序展示视觉上就是一个竖着的线路图效果很直观。有些同学还会对接高德地图把站点经纬度用Marker点在图上这个加分项我很推荐但注意需要去高德申请Web服务API的key流程很简单。4.3 后台管理界面表格表单弹窗后台管理页面用Element UI是最顺手的。线路管理页就是一个el-table列显示线路名称、类型、起终点、状态操作列里放“编辑”和“删除”按钮。点击新增或编辑弹出一个el-dialog里面放一个el-form表单表单校验规则写在data里比如线路名不能为空、首末班时间格式要匹配HH:mm。这里有个容易被忽略的点表单提交时要把数据深拷贝一份再传给接口不要直接拿表格里的row对象否则修改表单时表格里的内容也跟着变非常诡异。原因就是JavaScript对象引用传递的机制深拷贝用JSON.parse(JSON.stringify(row))就行。站点顺序维护这个功能我建议用el-transfer穿梭框或者一个可拖拽的列表来做。左侧显示所有可用站点右侧显示当前线路已选的站点右侧按顺序排列。提交时把右侧列表里的站点id和顺序一并传给后端。这个交互细节虽然简单但能让老师感觉你考虑得很周全不是只做了个最简单的下拉框。5. 项目部署与运行全流程5.1 本地环境准备很多同学拿到源码后第一步就卡在环境上我这里把版本组合列出来基本不会出问题工具推荐版本备注JDK1.8或11不要用JDK17跑SpringBoot 2.x容易出反射兼容问题Maven3.6.3配置阿里云镜像加速依赖下载Node14.x或16.xVue CLI 5支持良好MySQL5.7或8.05.7更省内存8.0特性新IDEA2021以上社区版也够用JDK的坑我说一下SpringBoot 2.7和JDK 17勉强兼容但有些老版本的MyBatis驱动在JDK17下会疯狂报错。为了省心JDK8或者JDK11是最好方案稳定不折腾。5.2 项目启动步骤后端启动方法用IDEA打开后端目录等待Maven下载依赖然后修改application.yml里的MySQL账号密码确保本地已创建数据库并在数据库中执行SQL脚本。最后运行TransitApplication主类控制台看到“Started TransitApplication”就说明启动成功。默认端口我在配置里设为8080如果你想改在application.yml里改server.port即可。前端启动方法进入前端目录执行npm install安装依赖。这里如果你网速不好npm会卡在node-gyp这种奇怪的包上我的建议是直接把npm源换成淘宝镜像npm config set registry https://registry.npmmirror.com npm install安装完成后执行npm run serve控制台会显示预览地址一般是http://localhost:5173或http://localhost:8080注意如果后端也是8080端口前端会冲突所以前端端口改成8081比较稳妥。5.3 前后端联调与数据初始化前后端分离项目必然要面对跨域问题。开发环境下最简单的方式是配置vue-cli的devServer代理。在vue.config.js里写module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端发到/api开头的请求都会被代理到后端绕开了浏览器的同源策略。生产部署时通常后端接口和前端静态文件都挂在同一个Nginx下再由Nginx转发这也是标准方案。数据初始化方面除了导入建表SQL还应该创建一个默认管理员账号。密码不要明文存储用BCrypt加密后写入数据库这一点在答辩时是很好的亮点。如果不想自己写代码生成加密密码可以在SpringBoot启动时写一个CommandLineRunner检测到没有管理员就自动初始化一个方便又省事。6. 常见问题与排查技巧实录6.1 接口跨域问题跨域是前后端分离项目最常遇到的初学者问题。表现是浏览器控制台出现CORS error或者Access to XMLHttpRequest at ... has been blocked by CORS policy。解决办法有好几种我实际测试下来最稳的是在后端配置全局CorsFilterConfiguration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOrigin(*); config.addAllowedMethod(*); config.addAllowedHeader(*); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }需要注意addAllowedOrigin(*)不能和携带Cookie的请求同时使用但毕设项目基本不涉及Cookie所以无伤大雅。如果用完这个配置还是报跨域就看看有没有多个配置类被重复加载或者检查拦截器是否拦截了OPTIONS预检请求。6.2 数据库中文乱码问题中文乱码通常出现在两个位置一是页面显示查询结果时出现??二是往数据库手工插入中文时报错。第一种情况多半是JDBC连接串没加编码参数第二种情况多半是建数据库时没有指定默认字符集。JDBC连接串里一定加上jdbc:mysql://localhost:3306/transit?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai建库时指定CREATE DATABASE transit DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;utf8mb4比utf8更强的地方在于它可以存emoji而且兼容所有中文字符所以现在的主流做法都是用它。6.3 Vue打包后刷新404问题这个问题几乎每个用Vue Router做history模式的同学都会遇到。本地npm run serve一切正常但前端打包成静态文件放到Nginx后点击其他路由是好的一刷新页面就404。原因是history模式的路由需要服务器配合做重写Nginx配置加一段location / { try_files $uri $uri/ /index.html; }如果你不想折腾Nginx最简单的方案是改用hash模式。在router/index.js里写new Router({ mode: hash, ... })打包后刷新就不会出现404。hash模式唯一的缺点就是地址上会多个#号但对于项目演示来说完全够用。6.4 MyBatis查询结果映射为空这也是个很常见的坑。数据库字段是station_name实体类属性是stationNameMyBatis默认下划线不转驼峰结果就是查询返回的stationName全是null。解决办法是在application.yml里配置mybatis: configuration: map-underscore-to-camel-case: true如果你用的是MyBatis-Plus这个配置默认就是开启的但如果配置文件被覆盖了也容易出问题。排查的时候先看看控制台打印的SQL结果有没有值有值但实体类null那基本就是映射配置的锅。6.5 端口占用和依赖冲突启动SpringBoot时报Port already in use说明8080端口被占用。查端口占用的命令Windows下是netstat -ano | findstr 8080Linux/Mac下是lsof -i:8080。找到PID后杀掉就行或者干脆换个端口。依赖冲突常见于引入了一些花里胡哨的第三方包。我的经验是项目里的依赖越少越好只用你确实需要的Spring Web、MyBatis、MySQL驱动、Lombok、JWT解析器、Hutool工具类。别为了炫技引入一堆包到时候版本不兼容哭都来不及。7. 毕设答辩与项目扩展建议7.1 如何讲清楚你的项目答辩的时候不要一上来就念PPT讲什么是SpringBoot老师比你清楚。你要讲的是“你做了什么、怎么做的、遇到什么坑怎么解决的”。我的建议是准备一个demo数据现场演示从用户端输入起点和终点展示直达或换乘结果然后切到管理端改一条线路的站点顺序再回到用户端验证结果变化。这套流程下来整个系统的业务闭环就完整展示出来了。重点讲一下换乘查询的实现。老师可能会问“你的换乘算法原理是什么”你可以回答先查找经过起点站的线路集再查找经过终点站的线路集取交集为直达如果没有交集则枚举线路对找共同站点作为换乘站。这个思路简单清晰比回答“我调用了某某接口”更有技术深度也更能证明代码是你自己写的。还有一个加分项是数据库设计。讲清楚为什么用关联表而不是在表里存列表字符串讲清楚station_order的作用这体现了你对关系型数据库范式的理解。最好再带一句“如果以后需求变成实时公交定位我只需要在现有表结构上增加车辆表和轨迹表不需要推倒重来。”这句话会让老师觉得你的设计有可扩展性。7.2 扩展方向从毕设到实际产品做毕设不是终点我建议你在基础功能做完后往下面几个方向挑一个延伸一下哪怕只是做个简单原型也能让项目脱颖而出。第一个方向是实时公交定位。这个需要接入物联网数据或者模拟数据前端用一个可滚动的列表展示每辆车当前位置和预计到站时间后端用WebSocket推送。技术上不难但非常能体现你了解实时通信方案。第二个方向是历史数据分析和可视化。把线路客流、站点热度等数据用ECharts画成图表做一个数据看板页面。ECharts的折线图、柱状图、散点图搭配起来非常好看视觉冲击力强答辩时特别能抓眼球。第三个方向是做成小程序版本。用uni-app或者微信原生小程序重写客户端复用现有后端API。现在很多企业都在招全栈工程师能展示你在不同前端技术栈间的迁移能力对找工作也有直接帮助。我自己当年做完这个项目后给换乘算法加了一个简单的“最少步行距离”优化其实就是给站点坐标算一下欧氏距离作为换乘点选择的加权因子。虽然算法很粗糙但当时这个小小的改进让我在答辩时被老师多问了好几句也顺势拿了不错的成绩。所以别小看任何一个你能主动增加的点那就是你和别人不一样的地方。最后再分享一个实际体会做这类系统项目最大的敌人不是技术难度而是半途而废。我在做的时候也有过想把换乘算法换成百度地图API一了百了的冲动后来还是咬着牙把逻辑自己写完了。过程虽然痛苦但写完后你会发现自己对SQL联表、集合操作、事务控制的理解通透了不止一个级别。如果你也正在做类似的项目别急着抄代码用心把每一步跑通这个项目回馈给你的绝对远超一个答辩分数。这个系统做完之后你手里的这套SpringBootVue前后端分离的完整经验不管是放进简历还是用来面试聊项目都是很扎实的谈资。祝你的项目也顺利跑通答辩拿个好成绩。