独立全栈 AI 旅游项目 SmartTravel 开发复盘:微服务架构、AI 行程与线上落地全记录
文章目录
SmartTravel 智游 —— 全栈智能旅游平台
前言
本文完整记录单人独立开发上线智能旅游平台 SmartTravel 的全过程,从微服务架构搭建、大模型行程生成、支付对接,到线上内存调优、多层容灾降级实战,完整覆盖前后端、中间件、部署运维全链路开发经验,适合全栈开发者、毕业设计项目创作者参考借鉴。
一、项目简介
智游是一个 AI 驱动的旅行规划平台。用户用自然语言描述旅行愿望——比如"带父母去北京 5 天,预算 1.5 万,喜欢历史文化,腿脚不好不爬山"——系统自动生成包含景点、餐饮、住宿的完整行程。确认行程后在线预订酒店、门票、餐厅,走支付宝真实扫码付款,支付成功后短信通知用户。
核心体验闭环:输入目的地 → AI 生成行程 → 确认预订清单 → 支付宝付款 → 短信通知。
整个项目独立完成,前后端共 13 个容器微服务,部署在阿里云 ECS 上,公网可直接访问。
| 层级 | 技术 |
|---|---|
| 前端 | Vue 3 + TypeScript + Element Plus + Pinia + Vite |
| AI 引擎 | LangChain + DeepSeek V3 |
| 后端 | FastAPI + SQLAlchemy 2.0 + Pydantic |
| 数据库 | MySQL 8.4 + Redis 7.2 |
| 搜索引擎 | Elasticsearch 8.15 |
| 消息队列 | RabbitMQ 3.13 |
| 部署 | Docker Compose(13 个容器)+ Nginx |
二、技术架构
整体拓扑
┌─────────────────────────────────────────────┐
│ Nginx (80) │
│ 反向代理 + 静态资源 │
└─────────────────┬───────────────────────────┘
│
┌─────────┴──────────┐
│ │
Frontend (5173) Gateway (8000)
Vue 3 + Vite FastAPI 统一网关
│
┌──────────┬──────────┬────┴─────┬──────────┬──────────┬──────────┐
│ │ │ │ │ │ │
User Itinerary Search Payment Notification Review
Service Service Service Service Service Service
(8001) (8002) (8003) (8004) (8005) (8007)
认证/短信 行程/AI ES搜索 支付宝 短信/邮件 评价系统
│ │ │ │ │ │
└──────────┴──────────┴──────────┴──────────┴──────────┘
│
┌───────────────┼───────────────┐
│ │ │
MySQL 8.4 Redis 7.2 RabbitMQ 3.13
│
Elasticsearch 8.15
Gateway 作为唯一入口,根据 URL 前缀路由到对应服务:
SERVICE_ROUTES = {
"/user": "http://user-service:8001",
"/itinerary": "http://itinerary-service:8002",
"/search": "http://search-service:8003",
"/payment": "http://payment-service:8004",
"/notification": "http://notification-service:8005",
}
拆成微服务的核心原因是各服务的资源特征不同:行程生成是 CPU/IO 密集型(调 LLM API 30-120 秒),搜索是 ES 密集型,支付是事务密集型。拆分后可以独立扩容。
低成本环境的内存调优
2 核 4G 运行 13 个容器(含 ES + MySQL),通过以下优化将内存占用控制在 3.2G 以内:
- Elasticsearch 堆内存:
-Xms128m -Xmx256m(默认 1G) - MySQL 调小 buffer_pool_size
- Python 微服务不设内存上限,按需分配
- 整体内存利用率约 85%,稳定运行不 OOM
三、核心功能实现
1. AI 行程生成:LangChain + DeepSeek V3
为什么选 DeepSeek V3
成本:API 价格约为 GPT-4o 的 1/50。一次行程生成消耗约 4000 tokens,成本约 0.01 元人民币。
中文能力:实测"带父母去北京,喜欢历史文化,腿脚不好不爬山"这种复杂约束,能正确提取"北京"+“历史文化”+“不爬山”+"无障碍需求"四个维度的信息。
API 兼容性:DeepSeek API 完全兼容 OpenAI 格式,一行配置即可接入:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
temperature=0.3,
max_tokens=4096,
timeout=120,
max_retries=2,
)
两阶段生成流水线
AI 行程生成拆成两个独立模块,各自有明确的输入输出契约:
用户输入 "我想一个人去新疆发呆5天,预算3000"
│
▼
┌──────────────────────────────────────┐
│ 阶段一:意图解析 │
│ 输入: 自然语言 │
│ 输出: { destination: "新疆", days: 5, │
│ budget: 3000, style: "自然风光",│
│ companion: "独自" } │
└──────────────┬───────────────────────┘
│
▼
┌──────────────────────────────────────┐
│ 阶段二:行程生成 │
│ 输入: 意图参数 │
│ 输出: 完整行程 JSON (每天活动/酒店) │
└──────────────────────────────────────┘
阶段一:用 LangChain 的 ChatPromptTemplate 约束 LLM 输出严格 JSON。LLM 偶尔输出非标准 JSON 时,代码先尝试 json.loads(),失败则用正则从原始文本中提取天数、预算、城市等关键信息,保证系统不崩溃。
阶段二:用 LangChain 的 @tool 装饰器定义 POI 搜索工具,LLM 在生成行程时自动调用高德地图 API 获取真实景点、酒店数据,基于真实数据填充行程。当高德 API 不可用时,降级为 LLM 根据常识自行补全。
2. 支付闭环
支付对接支付宝网页支付,实现从下单到回调的完整闭环。
用户确认预订清单 → 后端创建支付订单 → 跳转支付宝扫码付款
↓
支付宝同步回调(?paid=1)
↓
从 sessionStorage 恢复订单详情
↓
弹窗展示:订单号 + 费用明细(酒店/门票/餐饮逐项列出)
关键设计:
- 跳转支付宝前将订单快照存入
sessionStorage - 同步回调(return_url)负责页面跳转,异步回调(notify_url)负责支付确认
- 沙箱模式支持模拟支付,方便开发调试
踩坑:回调后页面曾出现空白,根因是 router.replace 导致 Vue 组件销毁重建。移除 replace 并改用 sessionStorage 做跨页面状态保持后解决。
3. Vue 3 前端架构
前端共 60+ 个组件,按功能分为六层:
views/ # 页面级组件
components/
├── chat/ # AI 对话:ChatPanel、StreamingIndicator、QuickPrompts
├── itinerary/ # 行程展示:TimelineDay、ActivityCard、BudgetBar
├── search/ # 搜索:SearchBar、SearchFilters、PoiCard
├── payment/ # 支付:BookingItem、PaymentPanel、ResourceLock
├── home/ # 首页:HeroSection、DestinationGrid、FeatureShowcase
└── common/ # 通用:AppHeader、AppFooter、LoadingSkeleton
TypeScript 类型系统:所有数据结构都有严格类型定义。一次重构把后端返回字段从 price 改成 estimated_cost,TypeScript 编译直接报了 12 个类型错误,精确指出每个需修改的文件。
Pinia 状态管理:拆了 4 个独立的 store(user、itinerary、search、payment),每个 store 只管理自己的状态和 API 调用,互不耦合。
JWT 双 Token 无感刷新:Axios 响应拦截器捕获 401 状态码,自动用 refresh_token 换新 access_token 后重试原请求。多个请求同时 401 时,用 isRefreshing 锁保证只刷新一次 Token。
SSE 流式输出:AI 生成行程耗时 30-120 秒,用 Server-Sent Events 实现流式推送。前端 fetch + ReadableStream 逐块读取,后端 FastAPI StreamingResponse + AsyncGenerator 逐个 yield SSE 事件,用户实时看到行程逐条生成。
4. 搜索引擎:Elasticsearch 8.15
支持三种查询模式:
全文搜索:multi_match 跨字段匹配,name 权重最高,带 fuzziness 自动容错:
{"multi_match": {
"query": "故宫",
"fields": ["name^3", "description^2", "tags^1.5"],
"fuzziness": "AUTO"
}}
地理位置搜索:geo_distance 查询附近 POI,按距离升序排列。
自动补全:completion suggest 类型,输入"故"自动提示"故宫博物院"。
MySQL 的 LIKE '%故宫%' 无法处理中文分词、拼音搜索、地理位置排序和相关性打分,这些 ES 全部内置支持。ES 不可用时自动降级到 Python 内存种子数据,保证前端不白屏。
5. RabbitMQ 事件驱动
支付超时处理用 x-delayed-message 插件实现 15 分钟延迟消息:
payment-service 创建订单
→ 发布 "order.created" 到 RabbitMQ
→ notification-service 消费 → 短信/WebSocket 推送通知
→ 延迟队列 → 15 分钟后检查支付状态 → 超时自动取消
6. 短信验证码三层降级
- 阿里云 SDK 调用失败 → 自动切换 dev_mode,生成本地验证码
- Redis 缓存失败 → MySQL 兜底(验证码同时写 Redis 和 MySQL)
- 全部失败 → 验证码直接通过 API 返回给前端
阿里云 SDK 是同步阻塞的,通过 loop.run_in_executor() 在线程池中调用,不阻塞 FastAPI 事件循环。
7. 资源锁防超卖
支付页面 15 分钟内锁定资源,超时自动释放。前端 setInterval 实时倒计时 900 秒,后端 Redis SET order_lock:xxx EX 900 到期自动过期,双重保障。
四、容灾设计:七层降级策略
系统中每一个外部依赖都有对应的降级方案:
| 故障场景 | 降级行为 |
|---|---|
| DeepSeek API 不可用 | 正则提取意图 + 模板生成行程 |
| 高德 POI API 不可用 | LLM 根据常识自行补全景点/酒店数据 |
| 高德天气 API 不可用 | 返回模拟晴天数据,不阻塞检查 |
| 阿里云 SMS 不可用 | 自动切换 dev_mode,验证码返回前端 |
| MySQL 不可用 | 服务不崩溃,切换内存字典模式 |
| Redis 不可用 | 验证码回退 MySQL,缓存穿透直接查 DB |
| Elasticsearch 不可用 | 返回空结果,自动补全用本地词典 |
这些异常处理逻辑约占整体代码量 30%,但它们是系统在生产环境真正可用的基础。核心原则:外部依赖挂了,自己的服务不能跟着挂。
五、故障排查实录
支付回调页面空白
- 现象:支付宝付款后跳回页面,主体区域空白
- 根因:
onMounted中router.replace('/itinerary/payment')导致组件销毁重建,弹窗一闪而空 - 解决:移除
router.replace,改用sessionStorage跨页面传递订单数据
重建前端后 Nginx 502
- 现象:
docker compose up -d --build frontend后页面报 502 - 根因:Nginx 缓存了旧容器的 IP,新容器 IP 变化后
Connection Refused - 解决:
docker compose restart nginx刷新 DNS 缓存
支付宝密钥目录混淆
- 现象:payment-service 启动崩溃,日志提示找不到密钥文件
- 根因:
alipay_private_key.pem和alipay_public_key.pem在 Git 中为空目录(Git 不追踪空目录) - 解决:通过 heredoc 在 ECS 上直接写入真实密钥内容
六、项目数据
| 指标 | 数值 |
|---|---|
| 独立开发 | 100%(1 人全栈) |
| 容器数量 | 13 个(7 微服务 + 6 中间件) |
| 前端组件 | 60+ 个 Vue 组件 |
| 功能闭环 | 注册→登录→AI 行程→预订→支付宝付款→短信通知 完整打通 |
| 内存优化 | 从 OOM 到稳定运行 3.2G / 3.7G |
| 降级层数 | 7 层(DeepSeek / 高德 POI / 天气 / SMS / MySQL / Redis / ES) |
| 上线状态 | 公网可访问,支付宝真实扫码支付 |
七、技术反思
- 微服务边界要务实:7 个服务在 2C4G 下运维成本高于单体。若重来一次,会先单体上线验证核心流程,再按业务域渐进拆分
- 日志是第一排查工具:
docker compose logs --tail=50是使用频率最高的命令,所有线上故障均靠日志定位 - 第三方依赖必须降级:每个外部服务挂了都有应对方案,不把系统可用性寄托在第三方 SLA 上
- 测试是最大的技术债:目前依赖手动验收,缺少单元测试和集成测试,是后续优先补充的部分
- TypeScript 物超所值:一次字段名重构发现 12 处遗漏修改,编译期拦截比运行时排查高效得多
- 支付模块深度不足:目前仅完成支付宝基础扫码支付闭环,支付成功后订单详情的查询与展示、支付消息的实时推送(站内通知 / WebSocket 推送而非仅短信)尚未实现,用户支付体验还有较大提升空间
- 搜索能力可进一步增强:当前 ES 搜索依赖字段加权和关键词匹配,缺少语义理解能力。引入 RAG(检索增强生成)可以将搜索结果结合大模型做智能整合,让用户获得更精准、更自然的搜索回答
- 单点 Redis 存在风险:当前 Redis 是单实例部署,一旦宕机会导致验证码、资源锁、会话缓存全部失效。后续需要搭建 Redis 集群保障高可用
- 数据展示缺乏可视化:个人中心的订单记录、消费统计、历史行程等信息目前以纯文本列表展示,缺少图表和可视化看板,用户难以直观了解自己的旅行消费趋势
八、后续规划
基于当前项目现状与技术反思,后续将分三个优先级推进:
优先级 P0:核心体验增强
| 功能 | 说明 |
|---|---|
| AI 行程生成升级 | DeepSeek V3 实时推理替代当前模板降级方案,生成质量从"可用"提升到"惊艳" |
| 支付订单详情展示 | 支付宝支付成功后,用户可在个人中心查看完整订单详情(订单编号、费用明细逐项列出、支付时间) |
| 消息通知推送 | 基于 WebSocket 实现站内实时通知(支付成功、行程提醒、天气预警),类似微信消息弹窗的推送体验,替代当前单一的短信通知 |
| 个人中心数据可视化 | 引入 ECharts 图表展示消费趋势、目的地偏好、月度旅行频次等,让数据一目了然 |
优先级 P1:业务闭环扩展
| 功能 | 说明 |
|---|---|
| 购物车 | 用户可在行程生成后将多个景点、酒店、餐厅加入购物车,统一结算,支持增删改和批量下单 |
| 旅游推荐系统 | 基于用户历史行程数据和偏好标签,协同过滤推荐相似的景点和行程,提升转化率 |
| 订单管理后台(商家端) | 酒店、餐厅、景区商家可登录后台管理订单、查看营收数据、设置库存和价格 |
| RAG 增强搜索 | 引入 Milvus 向量数据库 + Embedding 模型,搜索结果经 LLM 二次整合,支持口语化自然语言搜索(如"适合带爸妈去的安静景点") |
优先级 P2:架构升级
| 功能 | 说明 |
|---|---|
| Redis 集群架构 | Redis Sentinel 主从 + 哨兵模式,实现自动故障转移,保障验证码、资源锁、会话缓存的高可用 |
| MySQL 读写分离 | 主库写、从库读,应对用户量增长后的数据库压力 |
| ES 独立节点 | 将 Elasticsearch 从当前容器中拆出为独立节点,避免与其他服务争抢内存和 CPU |
结语
一个完整上线的项目不仅是功能堆砌,更考验架构取舍、故障容错与低成本运维能力。本项目暴露出微服务拆分、缓存高可用等优化空间,后续迭代将补齐 RAG 检索、Redis 集群等能力,也希望本篇实战复盘能为做全栈、AI 类开发的开发者提供可复用的落地思路。
更多推荐


所有评论(0)