Skip to content

xuoh/TuLing

 
 

Repository files navigation

途灵 — AI 智能旅行搭子

基于 AI Agent 驱动的智能旅行搭子平台,为自由行用户提供 AI 旅行规划、智能问答、景点 / 餐厅 / 酒店探索与收藏、行程规划、图片纪念墙。

技术栈

层级 技术
前端 Vue 3 + TypeScript + Vite + Vue Router + Pinia + Element Plus + AMap JS API
后端 NestJS + Fastify + TypeScript + Prisma
数据库 PostgreSQL 16
缓存/限流 Redis 7
鉴权 JWT (Access + Refresh Token), HttpOnly Cookie
AI 大模型 OpenAI 兼容 API(流式响应 + SSE)
外部数据 高德 POI(兜底检索) + 和风天气
部署 Docker Compose

项目结构

TuLing/
├── client/                    # Vue 3 前端
│   ├── e2e/                   # Playwright E2E 测试
│   └── src/
│       ├── pages/
│       │   ├── login.vue / register.vue   # 鉴权
│       │   ├── home.vue                   # 首页(旅行快照 + 热门目的地)
│       │   ├── chat.vue                   # 智能问答
│       │   ├── spots/                     # 景点(列表 / 详情 / 评价中心)
│       │   ├── restaurants/               # 餐厅(列表 / 详情)
│       │   ├── hotels/                    # 酒店(列表 / 详情)
│       │   ├── favorites.vue              # 统一收藏中心(景点 / 美食 / 酒店)
│       │   ├── trips.vue                  # 行程规划(AMap + LLM 文案)
│       │   └── albums.vue                 # 图片纪念墙 + 相册集
│       ├── layouts/           # AppLayout, AuthLayout
│       ├── router/            # Vue Router + 路由守卫
│       ├── stores/            # Pinia auth store
│       └── shared/
│           ├── api/           # Axios 封装 + Token 刷新 + SSE 流式客户端
│           ├── components/    # NodeDetailDialog / RouteMapDialog
│           └── types/         # TypeScript 类型定义
├── server/                    # NestJS 后端
│   ├── prisma/                # Prisma Schema + 数据迁移
│   ├── uploads/               # 用户上传图片落盘目录(运行时生成)
│   └── src/
│       ├── auth/              # AuthModule(注册/登录/登出/验证码/Token刷新)
│       ├── chat/              # ChatModule(会话管理/消息流/智能总结/推荐)
│       ├── user/              # UserModule
│       ├── spot/              # 景点(搜索 + 高德兜底 / 收藏 / 评价)
│       ├── restaurant/        # 餐厅(搜索 + 高德兜底 / 收藏 / 评价)
│       ├── hotel/             # 酒店(搜索 + 高德兜底 / 收藏 / 评价)
│       ├── trip/              # 行程(增删改查 + AI 文案)
│       ├── memory/            # 图片纪念墙(上传 / 列表 / 批量删除 / 移动)
│       ├── album/             # 相册集(增删查)
│       ├── weather/           # 和风天气代理
│       ├── prisma/            # PrismaModule(数据库服务)
│       ├── redis/             # RedisModule(缓存/验证码/限流)
│       ├── provider/          # ProviderModule(LLM 抽象层 + OpenAI 兼容适配器)
│       └── common/            # Guard, Decorator, Filter, Interceptor
├── scripts/                   # 运维脚本(setup/start/stop/cleanup/prod)
├── uml/                       # 用例图(PlantUML 源 + 渲染产物)
├── openspec/                  # OpenSpec 规格文档
└── docker-compose.yml         # PostgreSQL + Redis

快速开始

前置条件

  • Node.js >= 20
  • Docker Desktop

一键启动(推荐)

macOS / Linux:

bash scripts/setup.sh   # 首次:安装依赖 + 初始化环境
bash scripts/start.sh   # 启动所有服务(开发模式)

Windows PowerShell:

.\scripts\setup.ps1     # 首次:安装依赖 + 初始化环境
.\scripts\start.ps1     # 启动所有服务(开发模式)

启动后:

导入示例数据(首次启动后必做一次)

setup 脚本只完成依赖安装与数据库迁移,不会自动播种业务数据。如果不导入,景点 / 餐厅 / 酒店三个列表页都会是空的。首次启动后请执行一次:

macOS / Linux:

cd server && npm run seed:sample

Windows PowerShell:

cd server; npm run seed:sample

Windows CMD:

cd server && npm run seed:sample

该脚本读取仓库内置的 server/prisma/sample-data.json,向景点 / 餐厅 / 酒店三张表各插入 25 条离线示例数据,不依赖任何外部 Key。脚本采用 name + city 幂等更新,重复执行不会产生脏数据。

后续如需用真实高德数据刷新这份样本,先在已有数据库环境中导入真实数据,再执行 npm run export:sample-data 即可重新生成 sample-data.json 并提交。

注册与登录

数据准备好后,访问首页会先跳转到登录页。可以新注册账号,也可以直接用 demo 账号进入。

方式 A:注册新账号

  1. 访问 http://localhost:5173/register
  2. 依次填:邮箱 → 点「获取验证码」(60 秒冷却) → 用户名(≥2 字) → 密码(≥8 位,含字母和数字) → 勾选同意协议
  3. 提交后自动登录并跳转首页

验证码怎么收?

  • server/.env 配了 SMTP_*:验证码会通过邮件发送

  • 若未配置 SMTP(默认):验证码会打印在后端控制台,搜 [DEV] Captcha for 即可,格式:

    [Nest] LOG [AuthService] [DEV] Captcha for foo@example.com: 482931
    

    验证码 5 分钟内有效。

方式 B:使用内置 demo 账号

执行一次 seed:reviews 即可自动建出 6 个 demo 账号(同时会插入 50 条评价数据,便于演示):

cd server && npm run seed:reviews

此脚本要求数据库中至少已有景点数据,因此请确保先跑过 seed:sample

可用账号(统一密码 demo1234):

邮箱 昵称
demo.amy@tuling.app 旅行的Amy
demo.luke@tuling.app 背包客Luke
demo.linlin@tuling.app 林林爱拍照
demo.kai@tuling.app 凯子在路上
demo.cici@tuling.app 西西Travel
demo.yibo@tuling.app 一波看世界

注意:登录限流为 同 IP 连续失败 5 次 → 锁定 15 分钟,调试时请避免反复试错。

生产模式启动

将前端构建产物与后端打包为单端口(3000)服务,适合部署:

macOS / Linux:

bash scripts/start-prod.sh

Windows PowerShell:

.\scripts\start-prod.ps1

Windows CMD:

scripts\start-prod.bat

启动后访问 http://localhost:3000 即可使用完整应用。

脚本说明

脚本 macOS / Linux Windows (PowerShell) Windows (CMD) 用途
一键部署 bash scripts/setup.sh .\scripts\setup.ps1 scripts\setup.bat 安装依赖、初始化 .env、迁移数据库
一键启动(开发) bash scripts/start.sh .\scripts\start.ps1 scripts\start.bat 拉起 Docker + 后端(dev) + 前端(dev)
一键启动(生产) bash scripts/start-prod.sh .\scripts\start-prod.ps1 scripts\start-prod.bat 构建前端 + 后端生产模式(单端口 3000)
一键停止 bash scripts/stop.sh .\scripts\stop.ps1 scripts\stop.bat 停止服务,保留所有数据
一键清理 bash scripts/cleanup.sh .\scripts\cleanup.ps1 scripts\cleanup.bat 停止服务 + 删除数据和产物(不可逆)

setup 脚本本身不会播种业务数据,需要的话请参考上方「导入示例数据」或下方「种子脚本一览」。

种子脚本一览

所有种子脚本都在 server/ 目录下执行:cd server && npm run <script>

命令 用途 外部依赖 数据规模
seed:sample 推荐:景点 / 餐厅 / 酒店各 25 条离线示例数据 75 条
export:sample-data 读取当前数据库 Top 25 导出为 sample-data.json(用于刷新仓库样本)
seed:spots 全量内置景点(数据硬编码,但图片是失效占位 URL) ~30 条
seed:wuhan 武汉景点专项(含真实 POI / 图片) 需要 AMAP_WEB_KEY 武汉景点
seed:restaurants:wuhan 武汉餐厅专项 需要 AMAP_WEB_KEY 武汉餐厅
seed:hotels:wuhan 武汉酒店专项 需要 AMAP_WEB_KEY 武汉酒店
seed:reviews 给当前数据库的景点批量补 50 条评价(缺用户时自动建 demo 账号) 无(需先有景点) 50 条评价
seed:refill-images 给已有景点用高德 POI 照片补全 images 字段 需要 AMAP_WEB_KEY 更新已有数据

日常评审 / 演示走 seed:sample 即可;带 wuhan / refill-images 后缀的脚本是历史阶段性产物,需要自行准备高德 Web 服务 Key。

手动启动(备选)

展开手动步骤

1. 启动基础设施

docker compose up -d

2. 初始化数据库

cd server
cp .env.example .env
npm install
npx prisma migrate dev
npm run seed:sample

各 seed 脚本的差异详见上方「种子脚本一览」。seed:sample 不依赖任何外部 Key,适合首次跑通。

3. 启动后端

cd server
npm run start:dev
# → http://localhost:3000

4. 启动前端

cd client
npm install
npm run dev
# → http://localhost:5173

环境变量

参考 server/.env.example,关键配置项:

变量 说明 默认值
DATABASE_URL PostgreSQL 连接串 postgresql://postgres:postgres@localhost:5432/tuling
REDIS_HOST / REDIS_PORT Redis 连接 localhost / 6379
JWT_ACCESS_SECRET JWT Access Token 密钥 需自行设置
JWT_REFRESH_SECRET JWT Refresh Token 密钥 需自行设置
CORS_ORIGINS 允许的跨域来源(逗号分隔) http://localhost:5173
LLM_API_URL LLM API 地址(OpenAI 兼容) 需自行设置
LLM_API_KEY LLM API 密钥 需自行设置
LLM_MODEL LLM 模型名称 gpt-3.5-turbo
AMAP_KEY 高德服务端 Key(POI 兜底检索) 需自行设置
VITE_AMAP_KEY 高德 JS API Key(前端地图) 需自行设置(client/.env)
QWEATHER_KEY 和风天气 Key 需自行设置
SMTP_* 邮箱验证码 SMTP 配置 需自行设置

注意: 未配置 LLM / 高德 / 和风时,对应模块会降级或返回占位提示,不影响其余功能。

API 接口

认证 /api/auth

方法 路径 说明 鉴权
POST /api/auth/captcha 发送邮箱验证码 公开
POST /api/auth/register 注册新用户 公开
POST /api/auth/login 账号密码登录 公开
POST /api/auth/logout 退出登录 需登录
POST /api/auth/refresh 刷新 Token 公开
GET /api/auth/me 获取当前用户信息 需登录

智能问答 /api/chat

方法 路径 说明 鉴权
POST /api/chat/sessions 创建新会话 需登录
GET /api/chat/sessions 获取会话列表(分页) 需登录
GET /api/chat/sessions/:id 获取会话详情(含历史消息) 需登录
DELETE /api/chat/sessions/:id 删除会话 需登录
POST /api/chat/messages 发送消息(SSE 流式响应) 需登录
POST /api/chat/sessions/:id/summary 生成会话智能总结(SSE) 需登录
GET /api/chat/recommendations 获取推荐提问列表 需登录

景点 /api/spots

方法 路径 说明 鉴权
GET /api/spots 搜索景点(keyword/city/tags/sort),无结果自动降级到高德 公开
GET /api/spots/:id 景点详情 公开
GET /api/spots/favorites 当前用户的收藏列表 需登录
POST /api/spots/:id/favorite 收藏 需登录
DELETE /api/spots/:id/favorite 取消收藏 需登录
GET /api/spots/:id/reviews 景点评价列表 公开
POST /api/spots/:id/reviews 提交评价 需登录
GET /api/spots/my-reviews 我发布的评价 需登录
DELETE /api/spots/my-reviews/:reviewId 删除自己的评价 需登录
GET /api/spots/favorites-reviews 收藏景点的最新评价 需登录

餐厅 /api/restaurants

方法 路径 说明 鉴权
GET /api/restaurants 搜索餐厅(keyword/city/tags/cuisine) 公开
GET /api/restaurants/:id 餐厅详情 公开
GET /api/restaurants/favorites 我收藏的餐厅 需登录
POST /api/restaurants/:id/favorite 收藏 / 取消收藏 需登录
DELETE /api/restaurants/:id/favorite 取消收藏 需登录
GET /api/restaurants/:id/reviews 评价列表 公开
POST /api/restaurants/:id/reviews 提交评价 需登录
GET /api/restaurants/my-reviews 我的评价 需登录
DELETE /api/restaurants/my-reviews/:reviewId 删除我的评价 需登录

酒店 /api/hotels

方法 路径 说明 鉴权
GET /api/hotels 搜索酒店(keyword/city/tags/starLevel) 公开
GET /api/hotels/:id 酒店详情 公开
GET /api/hotels/favorites 我收藏的酒店 需登录
POST /api/hotels/:id/favorite 收藏 需登录
DELETE /api/hotels/:id/favorite 取消收藏 需登录
GET /api/hotels/:id/reviews 评价列表 公开
POST /api/hotels/:id/reviews 提交评价 需登录
GET /api/hotels/my-reviews 我的评价 需登录
DELETE /api/hotels/my-reviews/:reviewId 删除我的评价 需登录

行程 /api/trips

方法 路径 说明 鉴权
POST /api/trips 创建行程 需登录
GET /api/trips 行程列表(分页) 需登录
GET /api/trips/:id 行程详情 需登录
PUT /api/trips/:id 更新行程 需登录
DELETE /api/trips/:id 删除行程 需登录
POST /api/trips/narrative AI 文案生成(按日叙述 / 节点点评) 公开

图片纪念墙 /api/memories

方法 路径 说明 鉴权
POST /api/memories 上传图片(multipart/form-data,支持 title/location/takenAt/albumId) 需登录
GET /api/memories 图片列表(支持 albumId=<uuid> 过滤指定相册,albumId=none 仅未分类) 需登录
DELETE /api/memories/:id 删除单张图片 需登录
POST /api/memories/bulk-delete 批量删除(一次至多 100 张) 需登录
POST /api/memories/move 批量移动到相册(albumId: null 表示移到未分类) 需登录

相册集 /api/albums

方法 路径 说明 鉴权
GET /api/albums 我的相册列表(含封面 + 照片数) 需登录
POST /api/albums 新建相册集 需登录
DELETE /api/albums/:id 删除相册(级联删除相册内所有图片) 需登录

天气 /api/weather

方法 路径 说明 鉴权
GET /api/weather/forecast 多日天气预报(location / days 公开

鉴权方案

  • Access Token:15 分钟有效,承载于 HttpOnly Cookie
  • Refresh Token:7 天有效,SHA-256 哈希存储,可服务端吊销
  • 全局 Guard + @Public() 白名单模式
  • 前端 Axios 拦截器自动刷新 Token(并发请求排队合并)

已实现功能

  • 注册 / 登录(邮箱验证码、JWT 双 Token、退出登录、全局鉴权守卫)
  • 首页快照(行程 / 收藏 / 相册 / 会话总数 + 热门目的地 + 旅行灵感)
  • 智能问答(LLM 流式对话、SSE 实时推送、多轮会话管理、对话历史、智能总结、推荐提问)
  • 景点探索(高德 POI 兜底检索 + 关键词降级模糊匹配 + 标签多选筛选 + 收藏 + 评价)
  • 餐厅 / 酒店模块(同景点能力,含菜系 / 星级 / 价位筛选)
  • 统一收藏中心(景点 / 美食 / 酒店一处管理,Tab 切换 + URL 参数同步)
  • 行程规划(AMap 地图渲染 + 高德路径规划 + 景点 / 餐饮 / 住宿混合编排 + LLM 文案润色 + 天气信息)
  • 图片纪念墙(多格式上传 + 按时间 / 相册查看 + 批量删除 / 移动到相册)
  • 相册集(自建相册、相册级联删除磁盘文件、上传时归入指定相册)
  • 天气服务(行程页天气卡片、和风天气代理)
  • 网络部署(CORS 动态配置、Vite 局域网绑定、生产模式单端口托管、局域网 IP 展示)
  • 运维脚本(一键部署 / 启动 / 停止 / 清理,支持 Bash / PowerShell / CMD 三种终端)

测试

# E2E 测试(需要前后端运行中)
cd client && npx playwright test --config playwright.config.ts

About

基于AI Agent驱动的智能旅行搭子

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Vue 48.1%
  • TypeScript 43.1%
  • PowerShell 2.9%
  • Batchfile 2.7%
  • Shell 1.8%
  • CSS 0.7%
  • Other 0.7%