☰
基于SpringBoot+Vue+小程序的AI旅游攻略平台全栈实战解析
2026/10/6 3:21:35 网站建设 项目流程

最近刚把一套毕业设计项目完整整理出来——基于SpringBoot、Vue和小程序的旅游攻略平台,还接入了AI能力。这套东西说实话不是那种纯玩具Demo,攻略管理、线路规划、AI问答、小程序端、管理后台全都有,前后端代码都整理好了,项目名就叫“基于SpringBoot AI和Vue的旅游攻略小程序(源码)”。

当时做这个项目的原因其实很现实:很多人的毕设选题是旅游类的Web系统,但实际场景里大家更多用手机,而且现在AI那么火,如果所谓的“基于AI”只是挂个名号,答辩的时候一问就露馅。所以我的思路是,小程序端做成真正的用户入口,SpringBoot作为统一的业务后端,Vue做管理后台,再在AI能力上做几个真实落地场景——AI行程生成、AI攻略内容生成、AI问答助手。整套做完,无论你是用来当毕设,还是想自己练手学全栈,都有很直接的参考价值。

这篇文章我打算把整个项目的设计思路、核心实现、踩过的坑都捋一遍,尤其那些网上教程里不怎么写的细节——SpringBoot版本坑、Vue打包后塞进SpringBoot的静态目录、小程序的列表加载更多、AI请求的超时与频控处理、联调时的抓包和日志排查。都是实操经验,跟着走能少走很多弯路。

1. 项目整体设计与技术选型思路

1.1 三方端怎么分工

这个项目是典型的三端架构:微信小程序、SpringBoot后端、Vue管理后台。小程序是C端用户的入口,主要完成攻略浏览、搜索、路线收藏、AI生成行程、AI问答这些高频操作。管理后台是给运营或管理员用的,负责录入景点数据、审核攻略内容、配置AI参数、看用户反馈。SpringBoot后端则是中间枢纽,统一接数据库、Redis、AI模型API,把业务逻辑收敛到一处。

为什么小程序而不是H5?最重要的原因是旅游场景的“随手打开”属性,用户到了某个城市,想在微信里搜一下景点、问一句“某某区适合玩几天”,小程序从入口到使用的最短路径比H5要短很多。而且小程序里有原生地图组件和相关API,做景区位置展示、周边搜索比在浏览器里舒服得多。

Vue这边我用了Vue 3 + Vite + Element Plus。Vue 3的Composition API写业务逻辑更紧凑,Vite的启动速度实测比Webpack快几倍,开发体验好不少。管理后台不追求太花哨,重点是把数据的增删改查做清晰,配合富文本编辑器录入攻略内容,用ECharts展示一些统计信息。

1.2 AI能力应该落在哪些点上

AI不能只是一个聊天框,旅游攻略小程序里最值得做的AI场景我很早就想清楚了:第一是AI行程生成,用户输入“想去成都,4天3晚,喜欢美食和人文”,后端把问题交给大语言模型,让它按照指定JSON结构返回一份日程安排,程序解析后渲染成行程卡片和地图路点,这个价值比单纯聊天高很多。第二是AI攻略润色与生成,管理员在后台录入资料后,可以直接调用AI生成一段适合发布的攻略文案,再人工微调。第三是AI问答助手,用户在浏览景点时随时提问,比如“这个景区适合推婴儿车吗”,后端把该景点的介绍和用户问题拼装成提示词,让模型结合资料回答。

这里有一个关键设计:AI能力必须做一层封装。不要在前端直接调模型API,也不要在多个业务代码里各自拼Prompt。我单独抽象了一个AiService接口,底下根据实际需要可以切换不同实现,比如OpenAI兼容协议、各家国内模型网关等。业务侧只关心“输入一个结构,返回一个结构”,具体模型怎么选不影响业务流程。这样以后要换模型、加模型策略,都只改一个地方。

1.3 技术选型里容易被忽视的版本一致性

SpringBoot和Vue这两个生态版本迭代很快,选型时如果版本漂移很痛苦。我这套项目比较稳的组合是:SpringBoot 2.7.x + JDK 8 + MyBatis-Plus 3.5.x + Redis + MySQL 8,前端是Vue 3.4 + Vite 5 + Element Plus。SpringBoot 3虽然新,但对JDK版本和部分库的兼容要求更高,学生机和大部分云服务器上还是JDK 8最保险,所以我特意选了2.7.x而不是追新。Vue这边,Vite插件生态跟Vue版本基本绑定,尽量用官方维护的@vitejs/plugin-vue,少用那些个人维护的魔改插件,省得升级之后跑不起来。

注意:SpringBoot版本太高会遇到一些兼容问题,比如2.7升级到3.x后javax包变jakarta,很多老教程代码直接报错。我建议做毕设或学习项目就用2.7.x,配JDK 8,跑在阿里云或腾讯云的轻量服务器上都很稳。

2. 核心功能模块拆解与数据库设计

2.1 功能模块地图

整个项目的功能可以分成几个模块:用户模块负责小程序的微信登录、个人中心、收藏与足迹;内容模块负责景点资料的维护、图文攻略的编辑与发布;线路模块负责路线的创建、AI行程的生成与保存;互动模块负责问答与评论;AI模块负责所有模型调用、Prompt配置、结构化结果解析。每个模块都不是孤立存在的,比如AI生成行程之后,会先落到一个草稿状态,用户确认后再转成正式路线,这样避免模型返回的脏数据直接污染主表。

2.2 数据库表设计里的一些取舍

数据库我用MySQL,核心表大概有这些:user用户表、spot景点表、guide攻略内容表、route行程路线表、route_item路线详情表、question问答表、user_favorite收藏表。这里我特别想强调两点:

第一,景点和攻略之间我用了比较简单的设计,景点表只存基础信息(名称、简介、经纬度、封面图、城市编码、门票信息等),攻略内容表里存富文本正文,并通过spot_id关联景点。这样一条攻略可以核心关联一个景点,但又不需要搞复杂的多对多映射。如果你的项目要做多景点的专题攻略,可以再加一张关联表,但作为毕设来说,一对多已经足够说明数据关系了。

第二,AI生成内容要能追溯。我在路线表里加了source_type字段,标记数据来源是“人工创建”还是“AI生成”,还加了raw_ai_response字段存模型返回的原始JSON。好处很明显:一方面答辩的时候你能现场演示“这条路线是AI生成的,这是它的原始返回”,另一方面出问题排查时能快速定位是模型乱答还是前端解析错了。

CREATE TABLE `route` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) DEFAULT NULL COMMENT '创建人', `title` varchar(100) NOT NULL COMMENT '行程标题', `city_code` varchar(10) DEFAULT NULL COMMENT '城市编码', `days` int(11) DEFAULT NULL COMMENT '天数', `source_type` varchar(10) DEFAULT 'manual' COMMENT 'manual/AI', `raw_ai_response` text COMMENT 'AI原始返回JSON', `status` tinyint(4) DEFAULT '0' COMMENT '草稿/发布', `create_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='行程路线表';

第三,所有表的id都用bigint自增,时间字段用datetime,字符集统一utf8mb4。utf8mb4比utf8更省心,因为老版的utf8在MySQL里存不了emoji和部分生僻字,用户在前端打一个表情符号就能让存储报错,这个坑不想再踩第二次。

2.3 AI返回结果的结构化解析

大模型返回的内容是文本,但计算机程序需要的是结构。所以我在设计Prompt时,强制要求模型返回指定JSON格式,比如:

{ "title": "成都4日美食人文之旅", "days": 4, "schedule": [ {"day": 1, "title": "武侯祠-锦里-宽窄巷子", "spots": ["武侯祠", "锦里"], "notes": "建议下午去武侯祠,晚上逛锦里"}, {"day": 2, "title": "大熊猫基地-东郊记忆", "spots": ["成都大熊猫繁育研究基地"], "notes": "早点出发,上午熊猫最活跃"} ] }

后端拿到这个字符串后用Jackson解析成RouteDTO,再转成实体写入数据库。这里有个细节:模型偶尔会多输出一些文字,比如“好的,这是您的行程”这类前后缀,所以解析时不能直接JSON.parse(整段),要先做提取,把```json代码块标记或最外层的花括号部分截出来再解析。我的做法是写一个JsonExtractor工具类,优先匹配代码块,如果没有代码块就截取第一个{到最后一个}之间的内容,解析失败时兜底重试一次,换一个更严格的Prompt再调。

3. 后端SpringBoot实操要点

3.1 项目初始化的一个稳妥方案

后端我用Spring Initializr初始化的工程,依赖只选了Spring Web、MySQL Driver、Lombok,MyBatis-Plus和Redis自己手动加依赖。很多人这里会纠结要不要用Spring Data JPA还是MyBatis-Plus,我的建议是MyBatis-Plus,因为单表操作不需要写SQL,分页查询有现成的Page对象,对毕业设计的代码量来说体验非常友好。

如果一个新工程启动就报错,最常见的原因是SpringBoot自动配置尝试连接数据库失败了。所以我会在application.yml里先把数据源配置好,而且一定要把ddl-auto这类配置弄清楚。MyBatis-Plus这边把Mapper接口都继承了BaseMapper<T>,再写一个统一的MybatisPlusConfig,配置分页插件PaginationInnerInterceptor,这样后面的列表查询直接传Page参数就行。

3.2 统一返回结构和异常处理

前后端联调最烦的就是每个人返回的数据格式不一样。我在项目里定义了一个Result<T>类,统一code、message、data三个字段,成功返回200,业务失败返回自定义错误码,比如40001表示参数错误,40002表示数据不存在,50001表示AI服务超时。配合GlobalExceptionHandler,用@RestControllerAdvice捕获异常,统一转换成Result输出。这样小程序端和Vue端就只用处理一种结构,省掉大量兼容代码。

3.3 AI接口整合的完整流程

AI服务是整个项目的亮点,实现上我做了一个比较清晰的抽象:

  • AiProperties:读取配置中心或yml里的模型网关地址、API Key、模型名称、超时时间。
  • AiClient:底层HTTP客户端,用OkHttp封装对模型网关的调用,支持流式和非流式两种模式。实时问答我用流式,让效果“一个字一个字出来”,体验更好;行程生成这类长任务我用非流式,配合前端loading状态。
  • AiService:上层业务门面,负责构造Prompt,把返回的文本解析成结构化对象,并做异常兜底。

我在做AI行程生成的时候,发现很多模型对中文地理信息掌握不精准,生成的路线里会出现“上午去东郊记忆,下午去春熙路,然后去酆都鬼城”这种完全不合理的方案。所以我的Prompt里明确写了“只推荐用户指定城市范围内的景点,不得自行增加其他城市”,并且把城市编码对应的城市名称拼进Prompt。这个限制非常有效。

3.4 接口层面的频控与降级

模型API不是内部接口,有并发限制和费用成本,所以必须做频控和降级。我的方案是拦截器加Redis:用户在调用AI接口前,先根据userId查Redis里的计数,比如每分钟最多3次,每天最多30次,超了就返回提示“AI生成次数已用完”。这样不仅保护了后端钱包,也防止接口被脚本刷爆。

降级策略我也做了:当模型接口超时或返回错误时,后端先直接返回一个预先准备好的基础行程模板(不经过模型),提示“AI服务繁忙,已为您生成基础路线”。对小程序用户来说,至少还能得到结果,不至于页面白屏。这个逻辑在答辩时特别加分,因为评委能看到你不是只会调用一个API,而是考虑了异常场景。

4. 前端Vue管理后台与小程序端实现细节

4.1 Vue环境配置与工程化要点

Vue端开发环境其实最耗时间的是Node环境。Node版本太新或太旧都可能导致依赖编译不过,Vite要求Node 18+,但Node 22在某些老插件下也会有兼容警告。我的建议是直接用Node 18.20 LTS版本,配合pnpm安装依赖,pnpm install比npm快且磁盘占用小。生成项目用npm create vue@latest,选上Router、Pinia、ESLint,然后手动加Element Plus。

管理后台我做了几个页面:登录页、数据看板、景点管理、攻略管理、路线管理、AI配置。接口请求我用axios统一封装,拦截器里自动把后端的code处理了,非200时直接Message提示。路由守卫里判断token,没登录就跳到登录页。这些都属于常规操作,但能看出项目是否规范。

4.2 Vue打包后如何塞进SpringBoot

这是一个非常容易被忽略但特别实用的点。如果前端和后端分开部署,需要两台服务器或者一个Nginx反向代理,很多学生的云服务器就一台,配置Nginx又要填一堆东西。其实最简单的办法是把Vue打包后的dist目录直接复制到SpringBoot的src/main/resources/static下,这样SpringBoot启动后用8080端口既能访问后端接口,也能访问管理后台页面。

但直接复制会有一个大坑:前端路由如果用了history模式,用户在管理后台刷新页面时会404,因为SpringBoot处理不了前端的虚拟路径。解决办法有两个:一是Vite路由用hash模式,URL里带#,刷新不会有问题,最简单;二是后端写一个WebMvcConfigurer,把非接口路径都转发到index.html。我的项目里两个都做了,但如果你想少折腾,直接用hash模式就够了。

4.3 小程序端关键页面与列表加载更多

小程序端基于微信原生框架,没有用uni-app。为什么不用uni-app?因为这个项目只有微信端,用原生框架少一层编译转换,调试更直接,而且原生组件和小程序地图组件配合更顺畅。小程序里最核心的页面是首页、目的地列表页、攻略详情页、AI行程生成页、个人中心页。

列表加载更多是几乎每个项目都会遇到的功能,很多人的代码是一页一页地onReachBottom然后setData拼数组,但容易出现重复请求或者loading状态错乱。我建议做一个统一的请求工具函数:

async function loadList({ page, pageSize, resets }) { if (resets) { pageData.value = []; page = 1; loading.value = true; } const res = await request({ url: '/list', data: { page, pageSize } }); const rows = res.data.records || []; const newList = page === 1 ? rows : pageData.value.concat(rows); hasMore.value = rows.length === pageSize; pageData.value = newList; page += 1; loading.value = false; }

关键是hasMore的判定条件,如果当前页的返回条数不足pageSize,说明没有更多了,这时候再把onReachBottom里的请求直接拦掉。配合一个全局的loading标志位,防止用户快速滚动时并发请求导致的数据混乱。

4.4 地图与AI页面交互

路线图页面我用小程序的map组件,动态把行程中的景点坐标用markers渲染出来,点击marker弹窗显示景点名和当天安排。这里要注意,map组件的markers是一个数组,但每次setData全量更新可能卡,建议只更新变化的markers,或使用include-points把视野缩放到全部标记。

AI行程生成页面是从输入到结果的交互,我设计成三步:用户填目的地、天数、偏好标签;提交后进入生成中动画,轮询后端任务状态;生成完成后渲染行程卡片。轮询我用的是setInterval定时请求后端状态接口,因为模型生成一次可能需要十几秒,用一个长HTTP请求客户端容易超时,轮询反而可靠。

5. 常见问题、联调技巧与排错实录

5.1 联调抓包的实用方法

小程序端的网络请求排查比Web要麻烦一些,我通常用抓包工具来看小程序发出的HTTPS请求内容。具体做法:电脑和手机连同一个局域网,电脑上开启抓包代理并安装根证书,手机WiFi设置里配置代理指向电脑IP,然后在小程序开发工具关掉“校验合法域名”选项,就能看到完整的请求参数、返回数据、Cookies等信息。

这里有一个注意点:只抓包的代理端口是科研和调试的正常需要,但涉及线上生产环境的数据安全要谨慎,授权范围内调试。另外生产环境的小程序必须配置request合法域名,而且要HTTPS,不能直接写IP。

5.2 高频报错与排查速查表

我把这个项目开发过程中实际遇到的高频问题整理成了一张表,方便你对照排查:

现象可能原因解决办法
小程序请求后端一直超时域名没配HTTPS或不在合法域名列表开发工具勾选“不校验合法域名”;上线前配置合法域名和HTTPS证书
Vue打包后刷新页面404Vite路由用了history模式改用hash模式,或后端配置转发到index.html
AI返回的内容里出现乱码服务器JDK或MySQL字符集不是UTF-8数据库连接串加characterEncoding=utf8,表统一utf8mb4
分页数据重复未判空即拼接,或page重复自增统一列表加载工具,重置时将page置为1
SpringBoot启动内存溢出云服务器内存太小启动命令加-Xmx256m,或换轻量配置
小程序地图markers不显示经纬度字段类型不对或坐标为0打印坐标确认,后端转成字符串,确保经纬度有效
AI偶发超时模型响应太慢,HTTP连接超时太短OkHttp连接超时设60秒,读超时设90秒,加降级模板
Jackson解析AI结果失败模型返回了多余文字增加JsonExtractor提取校验后再解析
Vue后台登录后刷新丢失Token只存内存存到localStorage,Pinia初始化时读取
连不上MySQL云服务器安全组没放行3306安全组和防火墙都放行;生产环境建议用内网连接
部署后端后静态文件不更新打包的dist被缓存Nginx加协商缓存;SpringBoot加版本号或时间戳参数

5.3 现场答辩演示的几个加分细节

如果你的目的是用这个项目毕业答辩,有几个细节我强烈建议注意。第一,提前关上小程序开发工具的缓存,清空后台日志,现场演示时从一个稳定的页面开始操作,别一上来就点AI生成(万一模型接口临时故障就尴尬了),先演示数据库里已有的攻略和路线,再把AI生成作为压轴。第二,准备好一份简单的演示脚本,每做一个操作就讲一句对应的技术点,比如“大家现在看到的是列表的加载更多,它是通过触底事件配合分页参数实现的”。第三,把数据库表结构、核心接口清单、项目目录结构各截一张图放PPT里,答辩时不用现场翻代码,往那一放就很有条理。

5.4 关于扩展方向与后续维护

这个项目其实是留了扩展空间的。热点里提到的SpringBoot整合Flink,可以理解为如果你想做用户行为分析,比如统计用户浏览了哪些景点、在某篇攻略上停留多久,可以把日志打到消息队列,再用Flink做实时统计。不过毕设的话不太建议往里加,因为Flink本身部署和编程门槛高,除非你有充足时间。更推荐的两个轻量扩展是:一是把攻略内容接入全文检索,增加搜索的候选词推荐;二是做一个分享海报生成接口,后端用Java生成带二维码的海报图,用户分享到朋友圈后扫码能直接进入对应攻略,这个小功能还挺招人喜欢。

维护方面,AI模型调用一定要在后台留一个开关,如果模型因为账户欠费或政策原因不能用了,管理员能一键切回“纯数据库检索”模式,保证系统核心功能不瘫痪。这个开关我在后台里做了,本质是AiProperties.enabled配置动态可刷新,很实用。

6. 源码使用与运行部署指北

6.1 拿到源码后怎么快速跑起来

整个工程结构一般是四个部分:travel-backend(SpringBoot后端)、travel-admin(Vue管理后台)、travel-miniapp(微信小程序)、doc或sql(数据库脚本和说明文档)。第一步先把sql目录下的脚本导入MySQL,把application.yml里的数据库名、用户名、密码改成自己的。第二步在travel-admin目录执行pnpm install && pnpm build,把打包后的dist复制到后端resources/static目录。第三步启动SpringBoot,试试后端接口通不通。第四步用微信开发者工具导入travel-miniapp目录,把app.js里的baseUrl改成你电脑局域网IP加端口,比如http://192.168.1.100:8080。

这里有个顺序问题:一定要先启动后端再把小程序连上去,不然小程序请求会直接失败。另外小程序端不支持HTTP明文,开发工具里需要勾选“不校验合法域名、web-view、TLS版本及HTTPS证书”,否则请求会被拦截。

6.2 部署到云服务器的简化方案

云服务器部署我用的是最省事的方案:安装JDK 8和MySQL,把SpringBoot打成jar包,用nohup java -jar后台运行。小程序端如果要真机预览,需要HTTPS地址,这里有两种做法:一是买一台有公网的服务器,用Nginx加SSL证书做HTTPS反向代理,把后端接口代理出去;二是在小程序管理后台配置request合法域名。如果只是毕业答辩,用开发者工具的模拟器演示就足够了,不需要真机调试。

6.3 源码里我觉得写得最值的部分

如果让我挑一个最值得读的文件,我选AI模块的调用与解析封装。很多初学SpringBoot的同学调用模型API都是直接在Controller里写一堆HTTP代码,参数解析和异常处理全都散落着。我把整个流程收敛成了:配置读取出Key、客户端统一发送、Prompt模板管理、结构化解析、降级兜底,每个文件职责很单一。比如新增一个“攻略润色”功能,只需要在Prompt模板里加一条,然后在业务层调AiService.generateGuideContent(spotInfo)就可以,代码非常干净。

除了AI模块,路线模块也值得一看。路线和路线明细是用事务控制的,一个行程包含多个日期、多个景点和备注,我用了@Transactional保证要么全部写入成功,要么全部回滚,不会出现“路线创建了但里面的景点是空的”这种脏数据。

写在最后的小体会

整套项目从设计到编码,其实每一步都踩了不少教科书没写过的坑。SpringBoot版本、Vite打包、小程序地图、AI解析这些细节单独看都不难,但组合在一起的时候,如果前期没有一个清晰的分工和技术选型,很容易陷入“每步都在填坑”的循环。我个人最大的体会是:做全栈项目,前期把数据结构和接口边界定义清楚,比写代码多花点时间都是值得的。数据库表字段想清楚了,前后端联调就不会天天改接口;AI返回的格式约定好了,解析代码一次写完不用反复调;技术版本锁定在稳定组合,就不会被兼容性问题反复折腾。这套旅游攻略小程序的项目源码,虽然定位是毕业设计,但如果你认真把它读透、跑通,其实等于把一个完整的商业闭环项目拆开看了一遍。后续你想加什么功能,比如预订酒店、拼团出行、语音导览,这个骨架都能接得住。

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

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

立即咨询