毕业设计选题就像开盲盒:题目选得好,答辩就成功了一半;题目选得偏,后面全是加班的眼泪。近几年越来越多的同学开始把“SpringBoot + 微信小程序 + AI大模型”组合在一起,做成智能校园导航系统。这套组合技术栈新、业务场景真实、代码量适中,对本科生来说既能展示工程能力,又能体现对AI方向的思考,是一个非常值得分析的选题方向。
本文会从一个可落地的毕业设计项目出发,完整拆解“智能校园导航系统”的设计思路、数据库建模、后端接口实现、微信小程序端实现、AI大模型接入方式,以及你在做类似课题时一定会遇到的坑。
如果你是计算机专业正在选题的同学,或者想在校招简历里加一个“AI+小程序”项目的开发者,这篇文章可以直接作为你的项目参考文档。
1. 为什么做“AI智能校园导航系统”
1.1 传统校园导航的痛点
很多学校占地面积大,教学楼、实验楼、图书馆、食堂、宿舍分布复杂,新生入学第一天迷路是常态。虽然有高德、百度地图等公共地图App,但校园内的小路、连廊、教学楼内部入口、临时施工围挡,这些信息与公共地图数据并不完全同步。再加上校园建筑门牌号、教室编号、各学院办公地点等信息高度本地化,外部地图App很难提供精确的“步行到XX教室”的路线规划。
另外,传统的校园导航系统往往只是把地图页面搬到网上,交互方式是“电子的静态地图”,用户需要自己在地图上找到目标点,再手动规划路线。这种体验本质上没有解决“用户不会查、不想查、查不到”的核心问题。
1.2 AI大模型带来的交互升级
AI大模型的价值在于把“人找信息”变成“信息找人”。
在智能校园导航系统里,用户不再需要知道某个楼的具体名称,只需要用自然语言表达需求,比如:
- “我想去图书馆三楼的自习室怎么走?”
- “离我最近的食堂在哪儿?”
- “行政楼在哪个位置?几点下班?”
- “从一教到体育馆步行要多久?”
这些需求如果靠传统搜索框来处理,需要用户输入精准地点名称;而接入AI大模型后,系统可以通过意图识别、实体抽取、语义匹配来理解用户,再结合校园POI(Point of Interest,兴趣点)数据和路径规划算法,返回精确位置和路线。
1.3 这套选题的技术含量
这个题目的技术栈不是凭空拼凑的,它具有一条清晰的技术链路:
- 微信小程序负责用户端展示和地图交互;
- SpringBoot后端负责业务逻辑、数据管理、接口路由;
- 数据库存储用户、POI、路径、问答日志等数据;
- AI大模型负责自然语言理解和智能问答;
- 路径规划算法(如Dijkstra、A*)处理校园路网最短路径计算。
从前端到后端再到AI,是一条完整的全栈链路,非常契合毕业设计“周期适中、模块完整、有创新点”的要求。
2. 系统功能与需求分析
写代码之前,先梳理功能。很多同学拿到题目就急着建表写接口,结果写到一半发现逻辑对不上,非常浪费时间。合理的做法是先画功能模块图,再逐步细化。
2.1 功能模块总览
从角色角度看,系统分为普通用户、系统管理员两个角色。核心功能包括:
| 模块 | 功能说明 | 使用角色 |
|---|---|---|
| 用户登录 | 微信授权登录、Token鉴权 | 用户 |
| 校园地图 | 地图展示、POI标记、当前位置定位 | 用户 |
| 智能导航 | 自然语言导航、地点查询、路径规划、路线展示 | 用户 |
| AI智能问答 | 校园信息问答、教学楼开放时间、食堂推荐 | 用户 |
| 收藏管理 | 收藏常用地点、查看历史记录 | 用户 |
| 反馈管理 | 用户上报错误地点信息、路线问题 | 用户 |
| 后台管理 | POI管理、路径节点管理、问答日志管理、用户管理 | 管理员 |
| 数据统计 | 搜索次数统计、热门地点排行 | 管理员 |
2.2 核心业务流程
以“用户查询去图书馆的路线”为例,整个流程可以拆成下面几步:
- 用户在小程序对话中输入一句话,例如“我想去图书馆”;
- 小程序调用后端
/api/nav/ask接口,把用户输入传给SpringBoot; - SpringBoot把用户输入发送给AI大模型,附带校园场景的Prompt提示词;
- AI大模型返回解析结果,包括意图类型和命中的地点信息;
- 后端拿到地点信息后,在数据库中查询对应的POI坐标;
- 后端调用路网路径规划算法,计算当前位置到目标位置的最短路径;
- 后端把路线坐标点集合、总距离、预计步行时间返回给小程序;
- 小程序通过
map组件绘制路线,并显示文字导航信息。
这个流程里,最具技术难度的两块:一是AI大模型如何稳定地抽取用户意图和地点实体;二是路径规划如何能真正返回一条能走的校内路线,而不是画一条直线。
3. 开发环境与项目初始化
如果是自己搭项目,推荐使用下面的环境组合。版本号可以根据你当时的最新环境调整,本文以常见的稳定组合为例。
| 工具 | 推荐版本/方案 |
|---|---|
| JDK | 1.8 或 11 |
| Spring Boot | 2.7.x |
| MySQL | 5.7 或 8.0 |
| MyBatis-Plus | 3.5.x |
| Redis | 5.0+(可选,用于缓存与Token) |
| 微信开发者工具 | 最新稳定版 |
| 大模型API | 国内大模型API通用格式 |
| Maven | 3.6+ |
| IDEA | 2023+ |
说明:AI大模型的接入方式有很多种,可以调用国内大模型厂商提供的API,也可以本地部署开源模型。毕业设计阶段更推荐使用API方式,开发效率高、效果稳定,但要注意控制调用频率与成本。
3.1 项目结构规划
后端工程建议按下面的结构来组织,功能清晰,答辩时也容易讲解:
campus-navigation ├── src/main/java/com/example/campusnav │ ├── common │ │ └── Result.java // 统一返回结果 │ ├── config │ │ ├── WebConfig.java // 跨域与拦截器配置 │ │ └── AiModelConfig.java // AI大模型配置 │ ├── controller │ │ ├── AuthController.java │ │ ├── PoiController.java │ │ ├── NavController.java │ │ ├── ChatController.java │ │ └── AdminController.java │ ├── entity │ │ ├── User.java │ │ ├── Poi.java │ │ ├── PoiCategory.java │ │ ├── RoadNode.java │ │ ├── RoadEdge.java │ │ ├── Favorite.java │ │ └── ChatLog.java │ ├── mapper │ ├── service │ │ ├── PoiService.java │ │ ├── NavService.java │ │ ├── ChatService.java │ │ └── UserService.java │ └── util │ └── JwtUtil.java └── src/main/resources ├── application.yml └── mapper3.2 创建SpringBoot工程并配置依赖
在pom.xml中添加核心依赖。注意MyBatis-Plus的版本要与Spring Boot版本匹配。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>campus-navigation</artifactId> <version>1.0.0</version> <name>campus-navigation</name> <description>AI智能校园导航系统</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.4.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> </dependencies> </project>application.yml中需要配置数据源、MyBatis-Plus、AI模型参数等。AI模型参数建议通过application-ai.yml单独管理,避免把密钥提交到代码仓库。
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/campus_nav?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/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.StdOutImpl ai: model: api-key: your-api-key endpoint: https://api.example.com/v1/chat/completions model-name: your-model-name这里有一个容易踩坑的地方:如果Spring Boot升到3.x,对应的JDK版本、Jakarta命名空间、MyBatis-Plus版本都会变化。建议毕业设计优先选Spring Boot 2.7.x,生态稳、资料多、踩坑成本低。
4. 数据库设计
数据库是整个系统的地基。校园导航系统的数据可以拆成几个核心实体:用户、POI地点、地点分类、道路节点、道路边、收藏记录、聊天日志。
4.1 核心表结构
用户表:
CREATE TABLE `sys_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `open_id` varchar(64) DEFAULT NULL COMMENT '微信openid', `nickname` varchar(64) DEFAULT NULL COMMENT '昵称', `avatar` varchar(255) DEFAULT NULL COMMENT '头像', `role` tinyint(4) DEFAULT 0 COMMENT '0-普通用户 1-管理员', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`open_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;POI地点表:
CREATE TABLE `poi` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(128) DEFAULT NULL COMMENT '地点名称', `category_id` bigint(20) DEFAULT NULL COMMENT '分类ID', `description` varchar(500) DEFAULT NULL COMMENT '地点描述', `address` varchar(255) DEFAULT NULL COMMENT '详细地址', `longitude` decimal(10,6) DEFAULT NULL COMMENT '经度', `latitude` decimal(10,6) DEFAULT NULL COMMENT '纬度', `floor` varchar(32) DEFAULT NULL COMMENT '所在楼层', `open_time` varchar(64) DEFAULT NULL COMMENT '开放时间', `contact` varchar(64) DEFAULT NULL COMMENT '联系电话', `image_url` varchar(255) DEFAULT NULL COMMENT '图片', `is_hidden` tinyint(1) DEFAULT 0 COMMENT '是否隐藏', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_category` (`category_id`), KEY `idx_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;道路节点表:
CREATE TABLE `road_node` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `node_name` varchar(64) DEFAULT NULL COMMENT '节点名称', `longitude` decimal(10,6) DEFAULT NULL, `latitude` decimal(10,6) DEFAULT NULL, `is_poi_entrance` tinyint(1) DEFAULT 0 COMMENT '是否为POI入口', `poi_id` bigint(20) DEFAULT NULL COMMENT '关联POI', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;道路边表:
CREATE TABLE `road_edge` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `start_node_id` bigint(20) DEFAULT NULL, `end_node_id` bigint(20) DEFAULT NULL, `distance_meter` int(11) DEFAULT NULL COMMENT '距离(米)', `walk_time_seconds` int(11) DEFAULT NULL COMMENT '步行耗时(秒)', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;收藏表、聊天日志表相对简单,不再赘述。聊天日志可以用来统计用户高频搜索地点,也可以作为论文里的数据分析素材。
4.2 为什么要建路网而不是直接用直线距离
校园内有很多建筑之间并没有直接通道,如果用两个POI之间的直线距离当路线,会穿过湖面、教学楼墙体或者施工区域。比较合理的做法是把校内可行走的道路抽成“道路节点 + 道路边”的拓扑结构,然后在拓扑图上计算最短路径。这就是经典的最短路径问题,可以选用Dijkstra算法或A*算法。
这一步是答辩时很有分量的技术点,也是和“普通地图标注系统”拉开差距的关键设计。
5. 后端核心模块实现
5.1 统一返回结果
为了方便小程序端处理数据,所有接口都返回统一的JSON结构:
// 文件路径:src/main/java/com/example/campusnav/common/Result.java package com.example.campusnav.common; import lombok.Data; @Data public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("success"); result.setData(data); return result; } public static <T> Result<T> error(Integer code, String message) { Result<T> result = new Result<>(); result.setCode(code); result.setMessage(message); return result; } }再配合一个全局异常处理器,接口代码就会干净很多。
5.2 AI大模型接入:意图识别与信息解析
这是整个系统的创新重点。后端需要把用户的自然语言输入发送给大模型API,同时要求AI返回结构化的JSON结果。
关键点在于提示词工程。大模型能力再强,如果Prompt不清晰,返回结果也会飘。为了便于后续解析,建议在系统提示词里明确要求AI返回固定结构。
你是一个校园导航助手。你的任务是根据用户的输入,判断用户的意图,并抽取地点实体。 意图类型包括: 1. navigate:导航到某个地点 2. query:查询某个地点的信息 3. recommend:推荐地点 4. chat:闲聊或其他问题 请严格返回JSON格式,不要包含多余文字: { "intent": "navigate", "poi_keywords": ["图书馆"], "raw_input": "我想去图书馆" }后端调用AI模型的代码思路如下(以HTTP方式调用通用接口为例):
// 文件路径:src/main/java/com/example/campusnav/service/ChatService.java package com.example.campusnav.service; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.*; @Service public class ChatService { @Value("${ai.model.api-key}") private String apiKey; @Value("${ai.model.endpoint}") private String endpoint; @Value("${ai.model.model-name}") private String modelName; private final RestTemplate restTemplate = new RestTemplate(); public JSONObject parseUserIntent(String userInput) { String systemPrompt = buildSystemPrompt(); Map<String, Object> systemMsg = new HashMap<>(); systemMsg.put("role", "system"); systemMsg.put("content", systemPrompt); Map<String, Object> userMsg = new HashMap<>(); userMsg.put("role", "user"); userMsg.put("content", userInput); Map<String, Object> body = new HashMap<>(); body.put("model", modelName); body.put("messages", Arrays.asList(systemMsg, userMsg)); body.put("temperature", 0.2); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(apiKey); HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers); ResponseEntity<String> response = restTemplate.postForEntity(endpoint, request, String.class); // 这里以常见OpenAI兼容接口返回结构为例 JSONObject result = JSON.parseObject(response.getBody()); String content = result.getJSONArray("choices") .getJSONObject(0) .getJSONObject("message") .getString("content"); // 防止大模型返回markdown包裹的JSON,需要清理 content = content.replace("```json", "").replace("```", "").trim(); return JSON.parseObject(content); } private String buildSystemPrompt() { return "你是一个校园导航助手。请从用户输入中提取意图和地点关键词。" + "如果地点名称不在已知列表中,直接使用用户提到的名称。" + "请返回JSON格式:{\"intent\":\"navigate\",\"poi_keywords\":[\"图书馆\"]}"; } }这里有两个实际开发中经常遇到的问题:
- 大模型返回的内容偶尔会带
```json包裹,直接解析会报错,所以要先清洗字符串。 - 模型输出的“地点名称”可能和数据库中的名称不一致,例如模型可能把“图书馆”识别为“校图书馆”,因此后端在匹配POI时不能只做等值匹配,建议用
LIKE '%关键词%'或者模糊匹配,必要时可以维护一份“别名映射表”。
5.3 路径规划算法实现
路径规划部分,我建议直接实现一个Dijkstra算法,代码量不大,并且可以让答辩更有深度。
// 文件路径:src/main/java/com/example/campusnav/service/NavService.java package com.example.campusnav.service; import com.example.campusnav.entity.RoadEdge; import org.springframework.stereotype.Service; import java.util.*; @Service public class NavService { /** * 计算最短路径 * * @param startNodeId 起点节点ID * @param endNodeId 终点节点ID * @param edges 所有道路边 * @return 经过的节点ID列表 */ public List<Long> findShortestPath(Long startNodeId, Long endNodeId, List<RoadEdge> edges) { // 构建邻接表 Map<Long, List<RoadEdge>> adjacency = new HashMap<>(); for (RoadEdge edge : edges) { adjacency.computeIfAbsent(edge.getStartNodeId(), k -> new ArrayList<>()).add(edge); adjacency.computeIfAbsent(edge.getEndNodeId(), k -> new ArrayList<>()).add(edge); } // 距离表 Map<Long, Integer> dist = new HashMap<>(); Map<Long, Long> prev = new HashMap<>(); PriorityQueue<Map.Entry<Long, Integer>> pq = new PriorityQueue<>(Comparator.comparingInt(Map.Entry::getValue)); dist.put(startNodeId, 0); pq.offer(new AbstractMap.SimpleEntry<>(startNodeId, 0)); while (!pq.isEmpty()) { Map.Entry<Long, Integer> entry = pq.poll(); Long nodeId = entry.getKey(); int currentDist = entry.getValue(); if (nodeId.equals(endNodeId)) { break; } if (currentDist > dist.getOrDefault(nodeId, Integer.MAX_VALUE)) { continue; } List<RoadEdge> neighborEdges = adjacency.getOrDefault(nodeId, Collections.emptyList()); for (RoadEdge edge : neighborEdges) { Long nextNodeId = edge.getStartNodeId().equals(nodeId) ? edge.getEndNodeId() : edge.getStartNodeId(); int newDist = currentDist + edge.getDistanceMeter(); if (newDist < dist.getOrDefault(nextNodeId, Integer.MAX_VALUE)) { dist.put(nextNodeId, newDist); prev.put(nextNodeId, nodeId); pq.offer(new AbstractMap.SimpleEntry<>(nextNodeId, newDist)); } } } // 回溯路径 List<Long> path = new ArrayList<>(); Long cur = endNodeId; if (!prev.containsKey(endNodeId) && !cur.equals(startNodeId)) { return Collections.emptyList(); } while (cur != null) { path.add(cur); cur = prev.get(cur); } Collections.reverse(path); return path; } }然后在NavController中调用这个服务,把节点ID翻译成经纬度坐标返回给小程序。
5.4 POI搜索接口
POI搜索是导航的前置步骤,用户输入“图书馆”后,系统要能快速找到相关的POI记录。
// 文件路径:src/main/java/com/example/campusnav/controller/PoiController.java @RestController @RequestMapping("/api/poi") public class PoiController { @Autowired private PoiService poiService; @GetMapping("/search") public Result<List<Poi>> search(@RequestParam String keyword) { return Result.success(poiService.searchByKeyword(keyword)); } @GetMapping("/list") public Result<List<Poi>> list(@RequestParam(required = false) Long categoryId) { return Result.success(poiService.listByCategory(categoryId)); } }实际开发中,搜索接口还会加入分页、距离排序、热门排序等参数,毕业设计阶段可以先做基础版本,再在论文的“扩展方向”里写清楚后续优化点。
6. 微信小程序端实现
小程序端的核心页面包括:首页地图、对话导航页、地点列表页、个人中心页。
6.1 首页地图展示
利用微信小程序的map组件展示校园地图。示例代码如下:
<!-- 文件路径:pages/home/home.wxml --> <view class="page-container"> <map id="campusMap" latitude="{{currentLat}}" longitude="{{currentLng}}" markers="{{markers}}" polyline="{{polyline}}" scale="16" show-location="{{true}}" bindmarkertap="onMarkerTap" style="width: 100%; height: 60vh;" ></map> <view class="search-bar"> <input placeholder="输入目的地,例如:图书馆" confirm-type="search" bindconfirm="onSearchConfirm" /> <button type="primary" bindtap="onNavigateClick">导航</button> </view> </view>polyline是路线坐标点集合,小程序会直接在地图上绘制连线。需要注意的是,polyline的坐标点数量不能太多,需要对路径进行抽稀,否则部分低性能手机渲染会卡顿。
6.2 对话式导航页
用户打开对话页,可以直接输入自然语言。
// 文件路径:pages/chat/chat.js Page({ data: { messages: [], inputText: '' }, onInputChange(e) { this.setData({ inputText: e.detail.value }); }, async onSendMessage() { const text = this.data.inputText; if (!text) return; this.setData({ messages: [...this.data.messages, { type: 'user', content: text }], inputText: '' }); wx.request({ url: 'http://localhost:8080/api/nav/ask', method: 'POST', data: { input: text }, header: { 'Authorization': 'Bearer ' + wx.getStorageSync('token') }, success: (res) => { const data = res.data.data; this.setData({ messages: [...this.data.messages, { type: 'bot', content: data.reply }] }); if (data.path && data.path.length > 0) { this.setData({ polyline: [{ points: data.path, color: '#1A7AF8', width: 4, dottedLine: false }] }); } } }); } });注意:微信小程序访问本地localhost在本机调试时是可以的,但真机预览时必须把地址改为电脑局域网IP,并且在小程序后台配好合法域名或开启“不校验合法域名”的调试选项。
6.3 登录与Token管理
小程序端使用微信登录,调用wx.login获取code,再传给后端换取openid,后端生成JWT Token返回给小程序。后续请求在请求头Authorization中携带Token。
// 文件路径:src/main/java/com/example/campusnav/controller/AuthController.java @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private UserService userService; @PostMapping("/login") public Result<Map<String, Object>> login(@RequestBody Map<String, String> params) { String code = params.get("code"); String nickname = params.get("nickname"); String avatar = params.get("avatar"); // 调用微信接口获取openid // 实际开发中把appid和secret配置在yml中 String openid = getOpenIdFromWeChat(code); // 如果用户不存在则自动注册 User user = userService.loginByOpenId(openid, nickname, avatar); String token = JwtUtil.createToken(user.getId(), user.getRole()); Map<String, Object> data = new HashMap<>(); data.put("token", token); data.put("user", user); return Result.success(data); } }JWT工具类可以用java-jwt或jjwt来实现,核心是生成Token并校验签名。需要注意Token过期时间,实践中建议设置为7天,小程序端在收到401响应时自动跳回登录页。
7. 系统运行与验证
当后端和小程序都开发完成后,按下面步骤启动联调:
- 初始化数据库:执行
schema.sql和data.sql,填充测试POI数据和路网数据。 - 启动后端服务:在IDEA中运行
CampusNavigationApplication,看到端口8080启动成功,表示后端就绪。 - 启动小程序:用微信开发者工具导入小程序前端代码,APPID可以先用测试号。
- 联调登录:小程序调用
wx.login,后端返回Token,完成用户登录。 - 联调导航:在对话页输入“我想去知行楼”,查看AI是否识别出目标地点,路径规划是否返回路线坐标。
- 验证后台管理:管理员登录后,可以对POI数据进行增删改查。
建议在data.sql中插入至少10个校内POI和20个路网节点,这样演示效果更完整。
8. 常见问题与排查思路
做一个全栈项目一定会遇到各种问题。下面把这套项目最常见的坑整理成表格,方便大家对照排查。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 后端启动失败,提示数据库连接失败 | MySQL未启动、账号密码错误、数据库名不存在 | 检查MySQL服务,核对application.yml数据源配置,先创建数据库campus_nav |
| 小程序请求后端接口失败 | 本机调试用了localhost,真机用手机访问不到电脑 | 改为电脑局域网IP;开启微信开发者工具“不校验合法域名” |
| AI模型返回内容无法解析 | 模型返回了额外文字或Markdown格式 | 清洗返回内容,去掉```json标记;在Prompt里明确要求“必须返回JSON” |
| 路径规划返回空路径 | 起点或终点没有配置道路节点,路网不连通 | 检查road_node和road_edge数据,确保起点终点都在同一张连通图内 |
| Token过期,小程序接口返回401 | JWT过期时间太短,或前端没有拦截401 | 前端请求封装里统一处理401,跳转登录页重新登录 |
| map组件路线不显示 | polyline的points字段为空,或坐标格式错误 | 检查后端返回的坐标格式是否为[{latitude, longitude}],调整polyline属性 |
| 大模型API调用报错429 | 请求频率超过限制 | 在AI调用层增加限流、重试和缓存机制,避免重复解析同一类输入 |
这些排查技巧看起来零散,实际上都遵循同一个原则:从网络层、接口层、数据层逐层检查。排查问题时不要只盯着报错信息后面的几行日志,很多时候问题出在更上游的地方。
9. 部署与工程化实践
9.1 配置管理
大模型API密钥属于高敏感信息,绝对不能硬编码在代码里。开发环境可以放在application-dev.yml,生产环境要放到环境变量或配置中心。如果项目里有多个环境,建议按下面方式拆分:
spring: profiles: active: dev9.2 日志与审计
AI相关的调用建议单独记录一份请求日志,包含用户ID、原始输入、模型返回内容、响应耗时。一方面可以用于排查问题,另一方面可以作为论文里的实验数据。示例表结构如下:
CREATE TABLE `ai_query_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) DEFAULT NULL, `input_text` varchar(500) DEFAULT NULL, `model_response` text, `intent` varchar(32) DEFAULT NULL, `cost_ms` int(11) DEFAULT NULL, `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;9.3 AI调用成本控制
毕设阶段的AI接口调用量不会很大,但要防止用户恶意刷接口。建议在后端加入简单的频率控制:
- 同一用户每分钟最多调用5次AI接口;
- 对于重复或相似的问题,优先从Redis缓存中取历史结果;
- 对于包含“图书馆”“食堂”等高频地点的简单查询,可以直接通过规则匹配处理,不需要每次都请求大模型。
这种“规则优先、模型兜底”的策略既省钱,又能显著提升响应速度,适合写进论文的优化章节。
10. 毕业设计答辩要点与扩展方向
10.1 答辩时可能会被问到的问题
为什么选择SpringBoot而不是SSM? 可以从自动配置、生态成熟、内置Tomcat、与MyBatis-Plus整合方便等角度回答。
路径规划算法的时间复杂度是多少? Dijkstra算法使用优先队列实现时复杂度为
O((V+E)logV),在校园路网规模下完全够用。如果校园面积更大,路网节点增加到几万个,系统还能高效运行吗? 此时可以考虑引入A*算法、加入网格索引、把路网数据加载到Redis缓存等优化方式。
AI大模型解析结果不准确怎么办? 可以在Prompt中提供校园POI名单,让模型在候选集合中做抽取;同时维护别名库,提高匹配成功率。
系统安全问题怎么考虑? 后端对 Token 做统一拦截校验;AI接口做限流;数据库SQL全部使用预编译参数;管理端接口校验管理员权限。
10.2 可扩展的方向
如果学有余力,建议在基础功能之上尝试下面任意一个方向:
- 室内导航:在大型教学楼内增加楼层切换、室内路网规划;
- 实时路径优化:结合教室课程表和当前人流量,推荐不拥堵的路线;
- 语音导航:小程序端集成录音与语音识别能力,让用户直接说目的地;
- 多校区支持:在POI、路网表中增加校区字段,实现多校区切换。
扩展方向不需要全部实现,能在论文“系统展望”章节里写出“为什么可以这样设计”,就已经是加分项。
做毕业设计,不要抱着“先交差再说”的心态。SpringBoot、微信小程序、AI大模型这三个技术点,每一个都在企业实际开发中有广泛应用。把校园导航这个场景做完整,你收获的不仅是一个能通过答辩的项目,更是一整套从需求分析到前端展示再到算法设计的产品思维。本质上,这和真实企业里做一款LBS应用的逻辑是相通的。希望这篇拆解能帮你把系统搭起来,也能让你在答辩时讲得清楚、答得从容。