ARTICLE DETAIL

资讯详情

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

微服务+小程序+AI:智能公交查询系统设计与实现全攻略

微服务+小程序+AI:智能公交查询系统设计与实现全攻略 “微服务 小程序 AI”这三个词凑在一起几乎是这两年计算机毕业设计题目的顶配组合。但很多同学看到这种题目第一反应不是兴奋而是慌SpringCloud 那一堆组件还没搞明白微信小程序又要单独学一套前端语法AI 还得找个地方“硬塞”进去。最后做出来的东西要么是单体项目改了改端口硬撑微服务名头要么是小程序页面写得不错但后端只是简单的 CRUD答辩时老师一问服务治理就卡壳。这篇文章要做一个明确判断“智能交通微服务公交查询系统”这个题目真正值钱的地方不在于“查公交”这个业务本身而在于它天然形成了一条完整的微服务业务链路。用户端通过微信小程序发起请求请求经过网关进入微服务集群服务之间通过注册中心完成发现与调用数据经过缓存和数据库落盘再叠加一路 AI 能力作为差异化亮点。这一套流程走通你收获的不只是一个毕设作品而是一套可以迁移到任何企业级微服务项目中的工程思维。读完这篇文章你会得到一份可以照着做的完整思路系统怎么拆服务、数据库怎么设计、Nacos 怎么接入、小程序登录怎么不踩坑、AI 接口怎么优雅地“接进去”而不是硬凑功能。文章会给出可直接复用的代码片段、运行顺序、验证方法和答辩应对方向。如果你正在为毕业设计选题焦虑或者想在简历里多一个能讲清楚全链路的项目这篇内容值得你收藏后慢慢看。1. 这篇文章真正要解决的问题先说实话很多微服务方向的毕业设计最终都长成了同一个样子。后端是用 Spring Boot 写了几个模块然后复制粘贴改个端口启动多个实例就管自己叫“微服务架构”。前端是微信小程序套了一个模板调用几个后端接口。AI 部分则更加尴尬要么根本没有要么只是在某个页面放了一个“智能客服”按钮后面连的是写死的 JSON。这不是真正意义上的微服务项目更不是智能系统。那一个合格的、能拿去答辩的智能交通公交查询系统应该是什么样我觉得至少要做到三件事第一服务要真正拆分。用户登录、公交线路查询、站点搜索、路线规划、AI 智能助手这些能力应该分成独立的微服务模块通过注册中心互相发现通过远程调用完成协作而不是全部塞在一个 Spring Boot 工程里。第二链路要真正跑通。从微信小程序发起请求开始请求要经过统一的 API 网关网关要识别你是谁再把请求路由到对应的微服务。服务之间如果需要互相调用要能通过 OpenFeign 或者 RestTemplate 完成。用户登录要真正拿到微信的 openid而不是 hardcode 一个假用户。第三AI 要真正有功能落点。在这一类交通系统里AI 最适合切入的位置是“自然语言查询”和“出行建议”。比如用户输入“从人民广场到火车站怎么走”系统能解析出起点和终点然后调用路线规划服务返回换乘方案。这种 AI 能力是可以被老师问到实现逻辑的而不是一句“我们接入了大模型”一带而过。这篇文章的读者主要是有一定 Java 基础、但没完整做过微服务项目的在校学生。如果你现在正处于“知道 SpringCloud 有什么组件但不知道如何组合使用”的阶段这篇文章就是给你准备的。2. 系统架构从单体到微服务的本质变化先来看一个最容易被误解的问题微服务和单体到底差在哪里很多同学觉得微服务就是把原来的 Controller、Service、Mapper 按照业务模块分文件夹。这完全是错的。在单体项目里模块之间是同一个进程内的 Java 方法调用用户模块可以直接new UserService()然后调用里面的方法。微服务则不同每个服务都是独立的进程甚至可能部署在不同的服务器上服务之间靠网络通信。拿公交查询系统来说单体架构下的调用流程是这样的小程序请求 - Tomcat 里的 Controller - Service - Mapper - 数据库而微服务架构下的流程变成了这样小程序请求 - Nginx - Spring Cloud Gateway 网关 - 用户服务 / 公交服务 / 智能助手服务 - 各自的 Service 和 Mapper - 各自的数据库或共享数据库区别看似只是多了一层网关实际上是整个项目的组织方式发生了根本变化。每一个服务都可以独立开发、独立部署、独立扩容。比如临近早晚高峰所有用户都在查公交到站时间运维人员只需要把公交查询服务多启动两个实例而不用把整个系统重新部署一遍。这个系统的核心组件可以整理成下面这张表组件在本项目中的角色关键点Spring Cloud Gateway统一入口处理跨域、路由、鉴权不用自己写网关配置路由规则即可Nacos注册中心 配置中心服务实例信息注册到这里也管理配置文件OpenFeign微服务之间的声明式 HTTP 调用像调本地方法一样调用其他服务Redis缓存高频查询数据公交线路、站点信息可以缓存减轻数据库压力MySQL持久化核心数据用户、线路、站点、收藏等业务表微信小程序C 端用户入口负责登录、展示、交互大模型 API智能助手模块将用户自然语言转换为结构化查询意图用一句话总结架构设计背后的逻辑微服务不是把代码拆开就完事了而是把“进程内的调用”变成“跨进程的调用”所以你需要注册中心去管理服务地址需要网关去统一管控入口需要远程调用框架去降低调用复杂度。这个设计如果能在答辩时讲清楚比背十道微服务面试题都管用。因为面试题考的是知识点而这个是体系。3. 微服务拆分与数据库设计3.1 服务怎么拆服务拆分是微服务项目的第一步也是很多同学最容易拍脑袋乱拆的一步。拆服务的基本原则有两个一是按业务边界拆二是不要拆得太碎。公交查询系统整体规模不大建议拆成四个微服务模块服务名称职责主要接口user-service用户登录、openid 保存、用户收藏管理/api/user/**transit-service公交线路查询、站点查询、车辆到站信息/api/transit/**route-service起点到终点的换乘路线规划/api/route/**assistant-serviceAI 自然语言解析、智能出行建议/api/assistant/**这四个服务各自独立通过 Nacos 互相发现。用户在小程序里输入“从 A 到 B 怎么走”小程序请求先到网关网关路由到assistant-serviceassistant-service解析出起点和终点后通过 OpenFeign 调用route-service的接口最终把换乘方案返回给用户。为什么要单独拆一个assistant-service因为 AI 能力涉及大模型 API 调用这种调用往往比较耗时而且系统架构上属于外部集成。如果把它和其他服务混在一起一旦大模型 API 超时可能会拖垮整个服务。单独拆出来可以为后续扩展提供空间比如以后可以在这个服务里加更多 AI 能力而不影响核心查询链路。3.2 数据库表设计数据库是答辩时另一个高概率被追问的点。建议使用 MySQL核心表设计如下。用户表CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, openid varchar(64) NOT NULL COMMENT 微信小程序用户唯一标识, nickname varchar(32) DEFAULT COMMENT 用户昵称, avatar_url varchar(255) DEFAULT COMMENT 头像地址, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;公交线路表CREATE TABLE bus_line ( id bigint(20) NOT NULL AUTO_INCREMENT, line_name varchar(32) NOT NULL COMMENT 线路名称如 902路, start_station varchar(64) NOT NULL COMMENT 起点站, end_station varchar(64) NOT NULL COMMENT 终点站, first_bus_time varchar(8) DEFAULT 06:00 COMMENT 首班车时间, last_bus_time varchar(8) DEFAULT 22:00 COMMENT 末班车时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT公交线路表;公交站点表CREATE TABLE bus_station ( id bigint(20) NOT NULL AUTO_INCREMENT, line_id bigint(20) NOT NULL COMMENT 所属线路ID, station_name varchar(64) NOT NULL COMMENT 站点名称, station_order int(11) NOT NULL COMMENT 站点顺序, longitude decimal(10,6) DEFAULT NULL COMMENT 经度, latitude decimal(10,6) DEFAULT NULL COMMENT 纬度, PRIMARY KEY (id), KEY idx_line_id (line_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT公交站点表;这三张表是整个系统最核心的数据基础。如果项目想做得更完整还可以加user_favorite收藏表、bus_realtime车辆实时位置表等。但毕业设计不要一上来就把表设计得太复杂先把核心链路跑通再扩展。3.3 缓存设计公交线路和站点信息属于典型的“读多写少”数据非常适合用 Redis 做缓存。查询线路时先查缓存缓存未命中再查数据库然后回填缓存。这样可以显著提升接口响应速度也是答辩时一个很亮眼的性能优化点。思路如下小程序请求查询线路信息 - 查 Redis 缓存 key: bus:line:{lineId} - 命中则直接返回 - 未命中则查询 MySQL回填 Redis再返回这个逻辑很简单但要注意缓存穿透和缓存雪崩问题。比如恶意请求一个不存在的线路 ID每次都会打到数据库。解决方式是缓存空值或者使用布隆过滤器。在毕业设计中缓存空值是最容易实现的方案建议在代码里体现。4. 环境准备与项目搭建4.1 环境清单实操之前先把环境准备好。以下版本建议以实际项目环境为准本文章演示通用思路不写死具体版本号。工具用途说明JDK 1.8 或 11Java 运行环境主流 Spring Cloud 版本均支持Maven 3.6依赖管理使用 Maven 多模块结构MySQL 5.7 或 8.0主数据库创建bus_query数据库Redis缓存配置本地运行即可Nacos Server注册中心下载 Nacos Server 并启动微信开发者工具小程序调试需要注册微信小程序测试号IDEA开发 IDE建议直接使用 IDEA4.2 项目目录结构推荐使用 Maven 多模块结构父工程只做依赖管理不写业务代码bus-query-system/ ├── pom.xml // 父工程统一管理依赖版本 ├── bus-gateway/ // 网关服务 ├── user-service/ // 用户服务 ├── transit-service/ // 公交查询服务 ├── route-service/ // 路线规划服务 ├── assistant-service/ // AI 智能助手服务 └── sql/ └── init.sql // 数据库初始化脚本这种结构的优点是每个服务都是一个独立模块启动时可以单独启动整个项目结构非常清晰。论文里画架构图时这个目录结构可以直接映射到架构图上老师一看就明白你的设计思路。4.3 父工程依赖管理在父pom.xml中可以引入 Spring Boot 和 Spring Cloud Alibaba 的 BOM统一管理版本避免子模块之间出现依赖冲突dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version2.7.10/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2021.0.5.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement这里提醒一点SpringBoot 和 SpringCloud Alibaba 的版本兼容性非常关键一定要参考官方版本说明。版本不匹配时最常见的报错是 Nacos 注册不上或者启动时报 NoSuchMethodError。如果出现这种问题不要慌优先检查版本组合。5. 完整示例与代码实现这一章是整个项目的核心区。代码不会一次性贴完而是按功能模块讲解每个模块只保留最能说明问题的那部分。5.1 用户服务接入 Nacos 与配置管理每个微服务启动后都要注册到 Nacos让其他服务能发现自己。以user-service为例核心配置在bootstrap.yml中# 文件路径user-service/src/main/resources/bootstrap.yml server: port: 8081 spring: application: name: user-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 config: server-addr: 127.0.0.1:8848 file-extension: yaml启动类上只需要加上服务发现注解// 文件路径user-service/src/main/java/com/example/user/UserServiceApplication.java SpringBootApplication EnableDiscoveryClient public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }配置完成后启动服务打开 Nacos 控制台就可以在“服务管理”里看到一个名为user-service的服务实例。这一步验证通过说明服务注册功能已经正常。5.2 微信小程序登录后端接口微信小程序登录是这类项目里最容易出问题的一个环节但核心逻辑其实不复杂。小程序端调用wx.login()拿到一个临时code把这个code发给后端。后端拿着code去微信接口换取openid。拿到openid后查询数据库如果用户不存在就自动创建用户再返回一个自定义的登录态 Token。// 文件路径user-service/src/main/java/com/example/user/controller/UserController.java RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody LoginRequest request) { // 1. 通过 code 换取 openid 和 session_key String openid userService.code2Openid(request.getCode()); // 2. 根据 openid 查找或创建用户 User user userService.findOrCreateUser(openid); // 3. 生成登录态 Token简化版可以返回 userId 或 JWT String token userService.generateToken(user.getId()); return Result.success(token); } }真正的坑往往不在后端而在小程序端。常见的问题有前端wx.request没有配置合法域名导致请求直接失败真机调试时后端接口是localhost手机无法访问code传给后端时被 WXML 里的拼接语法截断等等。这些问题会在第七章集中说明。5.3 公交查询服务与 OpenFeign 远程调用公交线路查询本身就是一个普通的 CRUD 接口用 MyBatis-Plus 可以非常快速地实现。这里真正要演示的是微服务之间的远程调用。场景是assistant-service负责 AI 解析解析完成后需要调用transit-service查询线路信息。这时使用 OpenFeign 声明式调用// 文件路径assistant-service/src/main/java/com/example/assistant/client/TransitClient.java FeignClient(name transit-service, path /api/transit) public interface TransitClient { GetMapping(/line/{lineId}) ResultBusLineVO getLineDetail(PathVariable(lineId) Long lineId); }在assistant-service启动类上加上EnableFeignClients注解SpringBootApplication EnableDiscoveryClient EnableFeignClients public class AssistantServiceApplication { public static void main(String[] args) { SpringApplication.run(AssistantServiceApplication.class, args); } }使用 OpenFeign 的时候只要在接口里写上方法签名加上FeignClient注解Spring 会为这个接口自动生成代理对象。调用方不需要关心目标服务的 IP 和端口只需要知道服务名transit-service。服务地址的获取由 Nacos 自动完成。这块内容在答辩时是加分项。因为很多学生知道 OpenFeign 是什么但只有真正跑通过的人才能说清楚“使用 FeignClient 时为什么服务名必须与 Nacos 注册的服务名一致”。5.4 AI 智能助手服务AI 是这个项目的差异化亮点。为了避免“硬蹭 AI”建议把 AI 能力定位为“自然语言公交查询助手”。用户在小程序里输入一句话“从软件园到火车站坐几路车”。这个请求先到assistant-service。assistant-service需要完成两件事第一解析用户的出发地和目的地。这里有两种实现路线一种是直接调用大模型 API让模型输出结构化 JSON另一种是本地写一个包含关键词映射的简单解析器。推荐第一种因为代码更少且能体现 AI 能力。示意代码如下// 文件路径assistant-service/src/main/java/com/example/assistant/service/AssistantService.java public String parseIntent(String userInput) { // 调用大模型 API将 userInput 解析为结构化路线查询请求 String prompt 你是公交出行助手请从用户的描述中提取出发地和目的地 以 JSON 格式返回包含 start 和 end 字段。用户输入 userInput; String response aiClient.chat(prompt); // 解析响应 JSON得到 start 和 end // 再调用 route-service 查询换乘方案 // 最后将换乘方案整理成自然语言返回给小程序 return finalAnswer; }这里需要特别提醒安全边界问题大模型 API 的密钥绝对不能放在小程序前端。小程序代码是公开的任何人可以通过抓包拿到你的密钥会造成严重的安全风险和资金损失。密钥只能放在后端assistant-service中通过环境变量或者 Nacos 配置中心管理。答辩时如果老师问到这个点这是一个非常加分的回答。另外AI 接口调用可能耗时较长建议在代码中设置超时时间并做降级处理。大模型接口超时后系统可以回退到普通的关键词匹配方案而不是直接报错。5.5 微信小程序端请求封装小程序端最常见的写法是封装一个request.js统一处理请求地址、Token 注入和错误提示// 文件路径miniprogram/utils/request.js const BASE_URL http://localhost:8080 function request(url, method GET, data {}) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200) { resolve(res.data) } else { wx.showToast({ title: 请求失败, icon: none }) reject(res) } }, fail(err) { reject(err) } }) }) } module.exports { request, BASE_URL }在小程序开发阶段BASE_URL指向本机时需要勾选开发者工具里的“不校验合法域名”选项。但这里要特别提醒这个选项只用于本地开发调试上线前必须替换为 HTTPS 域名并配置合法域名白名单。如果这个问题处理不好小程序审核一定会被驳回。6. 运行步骤与效果验证拿到完整的代码之后最怕的不是代码看不懂而是不知道按照什么顺序启动也不知道启动之后怎么验证。这里给出推荐启动顺序步骤操作验证方式1启动 MySQL执行sql/init.sql初始化数据库show tables;能看到核心表2启动 Redisredis-cli ping返回 PONG3启动 Nacos Server浏览器访问 Nacos 控制台4依次启动 user-service、transit-service、route-service、assistant-serviceNacos 控制台出现 4 个服务实例5启动 bus-gateway 网关网关日志打印路由初始化信息6用微信开发者工具打开小程序登录、查询功能跑通运行成功后的关键效果在 Nacos 控制台“服务管理”中可以看到项目所有服务都注册成功。在小程序的登录页面点击登录控制台能看到user-service的请求日志数据库的user表中出现一条用户记录。输入公交线路编号能返回完整的站点列表。在 AI 助手里输入一句自然语言出行需求能得到换乘建议。如果某一个环节没跑通第一个排查方向是日志。微服务项目排错的核心思路是先定位是哪个服务出问题再去查对应服务的日志。控制台看到一大片红色报错时不要慌从最下面的 Caused by 开始看那才是问题的根源。7. 常见问题与排查思路这个项目踩坑最多的几个点集中整理成一张表格方便检索问题现象可能原因排查方式解决方案服务启动失败注册不上 NacosSpringBoot 版本和 SpringCloud Alibaba 版本不兼容查看启动日志中的异常信息按照官方版本对应关系调整依赖版本微信小程序登录失败前端没有正确获取 code或后台 appid 与 secret 不匹配在小程序端打印code在后端打印微信接口返回结果检查小程序测试号的 appid/secret 是否填写正确小程序真机调试请求失败后端接口是 localhost手机无法访问查看 request 请求返回的错误信息后端改为局域网 IP手机和电脑需要同一网络跨域请求被拦截网关未配置 CORS浏览器控制台查看跨域错误在 Spring Cloud Gateway 中统一配置跨域规则AI 接口响应超时大模型 API 耗时较长默认超时时间太短查看 assistant-service 的日志中调用耗时设置合理的连接超时和读取超时并增加降级策略数据库连接失败数据库账号密码与配置不一致使用数据库客户端直接连接测试检查配置文件中的 url、username、password接口返回数据中文乱码数据库编码不是 utf8mb4或接口未设置编码查看数据库表字符集建表时指定 utf8mb4接口返回设置 UTF-8特别注意微信小程序登录这个环节。很多同学遇到登录失败第一反应是去改后端代码但实际上一大半问题都出在配置上。微信小程序登录过程中可能出现一个错误信息里面包含appid与secret不一致、code过期、请求 IP 不在白名单等提示。你只需要记住一个原则这类问题先看微信接口返回的errcode和errmsg它已经指出了问题方向。8. 最佳实践与答辩建议8.1 工程层面毕业设计阶段就要养成一些好的工程习惯这些习惯在入职后同样受用。第一配置管理要统一。每个微服务都有连接数据库、配置 Redis 地址、配置服务端口等重复信息。推荐把这些公共配置放到 Nacos 配置中心统一管理。答辩时被问到“你是怎么管理微服务配置的”能顺利回答出来就是一个加分项。第二日志要打关键节点。同一个请求从网关到用户服务再到业务服务中间调用了哪些接口耗时多少推荐都记录下来。这样做有两个好处一是自己排查问题时能快速定位二是在线答辩或录屏演示时老师通过日志就能看到你的调用链路。第三数据库操作必须注意幂等和安全边界。比如用户收藏线路、写入收藏表之前先查询是否已经收藏过避免重复写入。AI 接口的密钥要放在后端环境变量里任何情况下都不允许出现在小程序代码中。8.2 答辩层面答辩的时候老师对这个项目的提问通常会集中在三个层次第一层项目是做什么的。要能在两分钟内讲清楚用户通过微信小程序查询公交信息系统通过微服务架构支持高并发场景AI 助手让查询方式更自然。第二层为什么用微服务。这是最容易翻车的问题。不能说“因为大家都用微服务”。正确的回答是公交查询系统在高峰时段查询并发大微服务架构可以把高频查询服务独立部署和扩容同时 AI 服务和大数据量查询服务隔离避免互相影响。第三层某个具体技术怎么实现的。比如 Nacos 的原理、OpenFeign 调用流程、小程序登录流程。回答时不需要背概念只要把你项目里的真实代码流程讲清楚即可。8.3 想继续提升可以做什么如果时间允许这个系统可以继续扩展几个方向接入实时车辆位置模拟数据让到站时间预测功能看起来更真实。把 AI 助手从“查询路线”扩展到“候车时间预测”。给网关增加简单的限流功能比如使用令牌桶算法。引入 Redis 缓存公交线路数据并做缓存命中率统计。每扩展一个点都会成为答辩时的加分项。9. 总结与后续学习方向现在回头看这个题目可以更清楚地看到一个事实这个毕业设计真正要求你掌握的不是某一个框架的 API而是微服务架构下的一条完整链路。从微信小程序发起请求到网关统一接入到微服务之间的注册发现与远程调用再到 AI 能力的外部集成整条链路上每个环节都有它存在的原因。你掌握了这条链路以后无论是写微服务工程师的简历还是面试时聊项目经历都会有底气。读者下一步可以按照文章的启动顺序把自己的项目跑起来。第一步先不要追求把所有功能都做完可以先把 Nacos 注册成功、小程序能登录、数据库能查出一条线路数据这就算完成了最核心的闭环。然后再逐步把 AI 助手接口接入把智能查询做成项目亮点。需要提醒的是微服务相关的知识范围很广SpringCloud Alibaba、Nacos、Gateway、OpenFeign、Sentinel每一个组件都可以单独学很长时间。这个项目不要求你把所有组件都吃透而是先把最常用的几个组合用明白。如果你在搭建过程中遇到版本冲突、服务注册不上、小程序登录失败这类问题不要急着怀疑自己能力不行这些问题是每个微服务初学者都会经历的阶段。把这篇文章里的排查思路用起来一步一个脚印把链路跑通你就已经超过大多数停留在“只写 CRUD”阶段的同学了。
返回列表