为什么是体育场馆
我自己除了写代码,剩下的时间基本都在健身房和球场上,所以第一个从 0 到 1 的项目就想做这个。 业务本身不新,场馆预约系统满地都是,我想要的是一条足够完整的后端主链路: 用户、场馆、场地、时段、订单、公告、评论、收藏、通知,每一环都得自己接一遍。 链路跑通之后,再往上叠 Agent 和 RAG——这样 AI 部分是长在真实业务上的,不是一个悬浮的聊天框。
先说预约这条主链路
真正的难点不在增删改查,在时段:
- 场地按日期 + 时间段开放,同一场地同一时段不能被约两次,冲突校验落在服务层,不依赖前端置灰;
- 前端做了一张预约时段可视化表,可约、已占、已停用一个颜色说清楚,不用用户自己算时间;
- 订单要能区分预约订单和商城订单,状态流转、取消、退款各自有流水记录(支付是模拟的,这点不装)。
外围接了 Redis(缓存、验证码、登录态)、MinIO(图片和文件)、Mail(预约/取消/退款通知)、 WebSocket、EasyExcel 导出,Maven 拆成 parent + admin / model / common 三个模块。 前端是 Vue3 + Element Plus + Vite,后台管理端和用户前台是两个工程, 本地跑起来分别是 3001 和 3002,后端在 9601。
Agent 只出草稿,确认权在人手里
这是整个项目里我最在意的一条设计。
用户说「帮我约明晚七点到九点的一个羽毛球场地」,Agent 会调工具去查场馆、查可预约时段, 然后生成一份预约草稿返回给前端;前端把它渲染成一张卡片,用户点确认按钮,才真正走预约接口。 中间那一步不能省——模型理解错了时间或者场地,代价是一次错误的真实预约。
配套还做了:
- 「我的预约」这类高频意图走直达路由,不把所有问题都塞给模型,准确性和响应速度都更好;
- 每次工具调用的名称、参数、结果都记进日志,带 traceId,后台能直接看到 Agent 到底干了什么—— 调试 AI 功能时,这一步比看代码有用得多。
RAG 用来回答「平台规则」这类问题
知识库走 PostgreSQL + pgvector:文档切片入库、向量化、检索、拼进提示词。 两个细节值得单独说:
- 命中来源展示:回答下面会列出标题、标签、知识范围、相似度和命中的文本片段。 没有来源的 RAG 回答,用户没法判断能不能信;
- 单条知识增量索引:后台改一条公告,不需要把整个知识库重跑一遍。
我知道哪里还不够
- 支付是模拟的,没有真实资金链路;
- 场馆别名(「奥体」到底指哪个馆)还没做解析,靠模型硬猜;
- 多轮上下文的裁剪比较粗,长对话会丢信息;
- 没有评测集。Agent 现在只能靠人工试,改一版提示词不知道是变好还是变坏—— 这也是我后来做 Job-Agent 时把 Eval 平台补上的原因。
怎么跑起来
需要 JDK 17、Maven 3.8+、MySQL 8、Redis 7、Node.js 16+。
先执行 db/gym.sql 初始化数据库,再把 Gym-admin/src/main/resources/application.yml 换成自己的环境,
敏感配置单独放在 application-local.yml(这个文件不进仓库),然后启动 AdminApplication 和两个前端工程。