Spring Boot 3 + Vue 3 全栈外卖系统:RabbitMQ异步解耦、MySQL/Neo4j双引擎推建

项目简介

本项目是一个完整的外卖点餐系统,采用前后端分离架构:

  • 后端:Spring Boot 3.2.5 + MyBatis-Plus + Redis + RabbitMQ + Neo4j + 支付宝沙箱
  • 前端:Vue 3 + Element Plus + Pinia + Vite

核心业务覆盖:用户注册登录、菜品浏览、购物车管理、下单结算、支付宝支付、订单状态实时同步、支付成功邮件通知等完整闭环。系统实现了基于用户行为的个性化推荐、基于图数据库的购物车共现推荐、以及基于消息队列的全链路异步解耦。


核心技术亮点

一、RabbitMQ 异步消息架构

系统使用 RabbitMQ 实现了 5 大业务场景的异步解耦,构建了完整的消息驱动架构。

1. Exchange / Queue 全景
Exchange(交换机) Queue(队列) Routing Key 业务场景
login_code_exchange login_code_mail login.code.mail 登录验证码异步邮件发送
order_pay_exchange order_pay_queue order.pay 支付成功异步邮件通知
order_delay_exchange order_delay_queue order.delay 订单超时延迟取消(TTL + 死信)
order_cancel_exchange order_cancel_queue order.cancel 超时订单实际取消执行
behavior_exchange user_behavior_queue behavior.save 用户行为异步采集与偏好更新
2. 消息流转机制
复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                        RabbitMQ 消息流转全景                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  【用户行为采集链路】                                                 │
│  前端行为上报 → BehaviorController → BehaviorServiceImpl             │
│       ↓                                                             │
│  behavior_exchange ──behavior.save──→ user_behavior_queue           │
│       ↓                                                             │
│  UserBehaviorConsumer 消费:                                        │
│    Step1: 写入 user_behavior_log 行为日志                           │
│    Step2: 更新 user_food_preference 偏好分值(加权累加)             │
│    Step3: 清除 Redis 推荐缓存                                       │
│    ↓                                                                │
│  ⚡ 降级策略:MQ不可用时自动降级为同步处理                           │
│                                                                     │
│  【订单超时取消链路(TTL + 死信队列)】                               │
│  下单成功 → 发送 OrderDelayMessage(30min TTL)                       │
│       ↓                                                             │
│  order_delay_queue (TTL=1800s, 死信转发至 order_cancel_exchange)    │
│       ↓ 30分钟后                                                   │
│  若订单未支付 → 死信路由到 order_cancel_queue                        │
│       ↓                                                             │
│  OrderCancelConsumer 消费 → 自动取消订单(status=3)                  │
│                                                                     │
│  【支付成功通知链路】                                                 │
│  支付宝回调 → OrderPayProducer → order_pay_queue                    │
│       ↓                                                             │
│  OrderPayConsumer 消费 → 构建 HTML 邮件 → 发送给用户                │
│                                                                     │
│  【验证码邮件链路】                                                   │
│  登录/注册 → CodeMailConsumer → 异步发送验证码邮件                   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
3. 降级容错机制
链路 降级策略 说明
行为上报 MQ不可用 → 同步处理 直接在当前线程执行日志写入+偏好更新+缓存清除
邮件发送 MQ投递失败 → 同步发送 保证邮件必达,不阻塞主流程
订单超时 MQ连接失败 → 不触发 订单依赖前端状态轮询兜底
全局异常 消费端异常仅记日志 不影响其他消息处理
4. 关键配置说明
ruby 复制代码
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
    listener:
      simple:
        concurrency: 1          # 最小消费者数
        max-concurrency: 3      # 最大消费者数
        acknowledge-mode: auto  # 自动确认
        prefetch: 10            # 每次预取消息数
    publisher-confirm-type: correlated   # 发布确认
    publisher-returns: true              # 发布返回
5. 核心 Consumer 实现
Consumer 监听队列 核心职责
UserBehaviorConsumer user_behavior_queue 消费行为消息,写日志+更新偏好+清缓存
OrderPayConsumer order_pay_queue 消费支付成功消息,构建HTML邮件并发送
OrderCancelConsumer order_cancel_queue 消费超时取消消息,自动将订单置为已取消
CodeMailConsumer login_code_mail 消费验证码消息,异步发送验证码邮件

二、MySQL 推荐引擎

系统基于 MySQL 构建了 用户行为权重推荐引擎,通过多维度用户行为数据生成个性化推荐。

1. 行为权重模型

系统为每种用户行为类型定义了权重值,用于计算用户对菜品的偏好分值:

行为类型 类型编码 权重值 说明
浏览 (BROWSE) 1 0.3 轻度兴趣,权重最低
加购 (CART) 4 1.5 中等意愿
收藏 (FAVORITE) 2 3.0 强偏好意愿
下单 (ORDER) 3 8.0 真实消费行为,权重最高

偏好分值计算公式:

复制代码
用户对菜品A的偏好分 = Σ(每次行为类型对应权重)

例如:用户先后浏览了菜品A(+0.3)、加购了菜品A(+1.5)、最终下单了菜品A(+8.0),则最终偏好分 = 0.3 + 1.5 + 8.0 = 9.8

2. 核心数据表
数据表 用途 关键字段
user_behavior_log 用户行为日志(原始记录) user_id, food_id, behavior_type, create_time
user_food_preference 用户菜品偏好分(聚合结果) user_id, food_id, score, update_time
resfood 菜品基础信息 fid, fname, realprice, category, spicy_level
order_item 订单明细(菜品销量统计) order_id, fid, num
3. 推荐算法流程
复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                     MySQL 个性化推荐流程                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Step1: 缓存查询                                                    │
│  ┌─────────────────────┐                                            │
│  │ Redis GET            │ → 命中 → 直接返回(TTL=2h)               │
│  │ recommend:v2:food:   │                                            │
│  │ user:{userId}        │ → 未命中 → 进入 Step2                     │
│  └─────────────────────┘                                            │
│                                                                     │
│  Step2: 用户画像判断                                                │
│  ┌─────────────────────┐                                            │
│  │ 查询 user_food_      │                                            │
│  │ preference 表       │ → 有数据 → 老用户,offset=0                │
│  │ COUNT(*)            │ → 无数据 → 新用户,计算哈希偏移            │
│  └─────────────────────┘                                            │
│                          ↓                                          │
│              新用户哈希偏移算法:                                    │
│              offset = Math.abs(hash(userId)) % maxOffset            │
│              (maxOffset = min(3, totalFoods - limit))              │
│              → 保证不同新用户看到不同的热销菜品组合                  │
│                                                                     │
│  Step3: MySQL 推荐查询(核心 SQL)                                   │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ SELECT f.fid, f.fname, f.realprice, f.fphoto,               │    │
│  │        IFNULL(p.score, 0) AS score                          │    │
│  │ FROM resfood f                                              │    │
│  │ LEFT JOIN user_food_preference p                            │    │
│  │   ON f.fid = p.food_id AND p.user_id = #{userId}            │    │
│  │ LEFT JOIN (菜品总销量子查询) s ON f.fid = s.fid              │    │
│  │ ORDER BY                                                    │    │
│  │   CASE                                                      │    │
│  │     WHEN 用户有偏好 AND p.score IS NULL THEN -1  -- 没点过排后 │    │
│  │     ELSE IFNULL(p.score, 0)                                  │    │
│  │   END DESC,                                                 │    │
│  │   总销量 DESC,                                               │    │
│  │   fid ASC                                                   │    │
│  │ LIMIT #{offset}, #{limit}                                   │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                     │
│  Step4: 缓存写入                                                    │
│  ┌─────────────────────┐                                            │
│  │ Redis SET            │ → 写入缓存供下次使用                     │
│  │ recommend:v2:food:   │   (缓存异常不影响返回)                  │
│  │ user:{userId}        │                                            │
│  └─────────────────────┘                                            │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
4. 两种推荐模式对比
维度 个性化推荐 (/recommend/list) 全局热销推荐 (/recommend/hot)
数据源 user_food_preference + resfood + order_item resfood + order_item + resorderitem
排序规则 偏好分 DESC → 总销量 DESC → fid ASC 总销量 DESC → fid ASC
目标用户 已登录用户(有行为数据) 新用户 / 冷启动
缓存 Key recommend:v2:food:user:{userId} recommend:hot
降级策略 新用户哈希偏移取不同热销组合 直接按总销量排序
5. API 接口
接口 方法 说明
/recommend/list?userId=&size= GET 个性化推荐,按用户偏好分排序
/recommend/hot?limit= GET 全局热销推荐,按总销量排序
/behavior/add POST 上报用户行为(异步入MQ)

三、Neo4j 图数据库推荐

系统引入 Neo4j 图数据库,构建了 购物车双引擎推荐 能力,基于用户历史订单共现关系和菜品属性规则实现智能搭配推荐。

OFTEN_BOUGHT_WITH 共现关系:

共现 = 在同一个购物车 / 同一个订单里面,一起出现过 。 比如很多人的购物车同时放:素炒莴笋丝 + 炖鸡,这两个商品共同出现,就建立这条关系。

1. 图数据模型
复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                      Neo4j 图谱模型                                  │
│              以 "素炒莴笋丝" 为中心的图关系示例                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   (UserNode)                        (FoodNode)                      │
│   ┌─────────────────┐               ┌─────────────────────┐        │
│   │ user_id: "4"    │               │ fid: 5              │        │
│   │ username:"123d" │──ORDERED─────→│ fname: "素炒莴笋丝"  │        │
│   └─────────────────┘  orderCount:2  │ realprice: 12.00    │        │
│         ↓              lastOrderTime │ category: "COLD"    │        │
│         │              :2026-08-02   │ spicyLevel: 0      │        │
│         │                            │ totalSales: 12     │        │
│         │                            └─────────────────────┘        │
│         │                                     ↑                    │
│         │                                     │                    │
│         │        ORDERED {count:1}            │                    │
│         └─────────────────────────────────────┘                    │
│                                                                     │
│   (UserNode)                                    ↑                   │
│   ┌─────────────────┐                           │                   │
│   │ user_id: "2"    │      ORDERED {count:1}    │                   │
│   │  username:"123" │───────────────────────────┘                   │
│   └─────────────────┘                                              │
│                                                                     │
│                                                                     │
│          素炒莴笋丝 的共现关系(OFTEN_BOUGHT_WITH)                    │
│          ─────────────────────────────────────────                  │
│                                                                     │
│   (FoodNode)                              (FoodNode)                │
│   ┌─────────────────────┐                 ┌─────────────────────┐  │
│   │ fid: 20             │                 │ fid: 17             │  │
│   │ fname: "清炒油麦菜" │──OFTEN_BOUGHT──→│ fname: "西红柿蛋汤" │  │
│   │ category: "COLD"    │   _WITH        │ category: "SOUP"     │  │
│   │ spicyLevel: 0       │   count: 3     │ spicyLevel: 0       │  │
│   │ totalSales: 8       │               │ totalSales: 15      │  │
│   └─────────────────────┘               └─────────────────────┘  │
│                 ↑                                  ↑               │
│                 │                                  │               │
│                 │  OFTEN_BOUGHT_WITH {count: 2}    │               │
│                 └──────────────────────────────────┘               │
│                                                                     │
│   (FoodNode)                              (FoodNode)                │
│   ┌─────────────────────┐                 ┌─────────────────────┐  │
│   │ fid: 16             │                 │ fid: 26             │  │
│   │ fname: "蒜蓉西兰花" │──OFTEN_BOUGHT──→│ fname: "紫菜蛋花汤" │  │
│   │ category: "COLD"    │   _WITH        │ category: "SOUP"     │  │
│   │ spicyLevel: 0       │   count: 2     │ spicyLevel: 0       │  │
│   │ totalSales: 6       │               │ totalSales: 10      │  │
│   └─────────────────────┘               └─────────────────────┘  │
│                                                                     │
│   
└─────────────────────────────────────────────────────────────────────┘

节点类型:

节点标签 主键属性 业务含义 核心属性
UserNode user_id 用户节点 username
FoodNode fid 菜品节点 fname, realprice, fphoto, category, spicyLevel, totalSales

关系类型:

关系类型 方向 含义 属性
ORDERED UserNode → FoodNode 用户下单关系 orderCount, lastOrderTime
OFTEN_BOUGHT_WITH FoodNode → FoodNode 菜品共现关系 count(共现次数)
2. 购物车双引擎推荐

CartRecommendServiceImpl 实现了两个独立的推荐引擎,分别产出不同推荐理由的菜品:

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    购物车双引擎推荐架构                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  输入:购物车菜品列表 + 用户ID                                      │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │  引擎一:规则推荐(基于菜品属性匹配)                         │    │
│  │                                                              │    │
│  │  原理:从 Neo4j 查询购物车菜品的 category 和 spicyLevel       │    │
│  │  规则优先级:                                                │    │
│  │  ① 有热菜无主食 → 推荐主食 (STAPLE)                         │    │
│  │  ② 有辣菜无汤/饮品 → 优先推荐汤品 (SOUP),其次饮品 (DRINK)   │    │
│  │  ③ 有主食无汤品 → 推荐汤 (SOUP)                             │    │
│  │  ④ 热菜+主食+辣,无凉菜 → 推荐凉菜 (COLD)                   │    │
│  │  ⑤ 兜底 → 推荐销量最高的主食或汤                             │    │
│  │                                                              │    │
│  │  Cypher 示例:                                               │    │
│  │  MATCH (f:FoodNode {category: $category})                    │    │
│  │  WHERE NOT f.fid IN $excludeFids                             │    │
│  │  RETURN f.fid, f.fname, f.realprice, f.totalSales           │    │
│  │  ORDER BY f.totalSales DESC LIMIT 1                         │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │  引擎二:共现推荐(基于图数据库关系遍历)                     │    │
│  │                                                              │    │
│  │  原理:从购物车菜品出发,沿 OFTEN_BOUGHT_WITH 关系遍历        │    │
│  │  排序:共现次数(coScore) DESC → 菜品销量(baseSales) DESC     │    │
│  │  降级:无共现数据时回退到热销推荐                            │    │
│  │                                                              │    │
│  │  Cypher:                                                    │    │
│  │  MATCH (cart:FoodNode)-[r:OFTEN_BOUGHT_WITH]->(rec:FoodNode) │    │
│  │  WHERE cart.fid IN $excludeFids                              │    │
│  │    AND NOT rec.fid IN $excludeFids                           │    │
│  │  RETURN rec.fid, rec.fname, rec.totalSales AS baseSales,    │    │
│  │         sum(r.count) AS coScore                              │    │
│  │  ORDER BY coScore DESC, baseSales DESC LIMIT 1               │    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                     │
│  输出:两个推荐菜品(不同引擎产出,保证不重复)                     │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │ ruleBased:     { fname: "蛋炒饭", tip: "搭配主食更美味" }     │    │
│  │ coOccurBased:  { fname: "酸辣汤", tip: "经常和这些菜一起下单"}│    │
│  └─────────────────────────────────────────────────────────────┘    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
3. 图谱数据初始化与同步

启动时自动初始化Neo4jDataInitializer):

复制代码
项目启动
  ↓
清空 Neo4j 全部节点与关系(MATCH (n) DETACH DELETE n)
  ↓
同步 MySQL 用户表 → MERGE 写入 UserNode
  ↓
同步 MySQL 菜品表 → MERGE 写入 FoodNode(含 category, spicyLevel, totalSales)
  ↓
聚合已支付订单 → 构建 ORDERED 关系(orderCount, lastOrderTime)
  ↓
统计菜品共现 → 构建 OFTEN_BOUGHT_WITH 关系(count = 共现次数)
  ↓
图谱就绪,推荐服务可用

运行时全量同步Neo4jSyncController):

接口 方法 说明
/sync/neo4j/all POST 全量同步 MySQL 数据至 Neo4j(幂等写入)
/cart-recommend/sync-co-occurrence POST 手动触发重建菜品共现关系
4. 三种推荐策略对比
维度 MySQL 个性化推荐 Neo4j 规则推荐 Neo4j 共现推荐
数据源 user_food_preference FoodNode (category) OFTEN_BOUGHT_WITH 关系
算法核心 行为权重累加 + 偏好分排序 品类补缺规则引擎 图遍历 + 共现次数排序
输入 用户ID 购物车菜品列表 购物车菜品列表
输出 4-10 个推荐菜品 1 个规则匹配菜品 1 个共现匹配菜品
适用场景 首页个性化推荐 购物车搭配推荐 购物车搭配推荐
优势 实时反映用户偏好 可解释性强 发现隐藏关联

技术栈

后端技术栈

技术 版本 说明
Spring Boot 3.2.5 核心框架
MyBatis-Plus 3.5.5 ORM 框架
Spring Security - 安全框架
MySQL 8.x 关系型数据库(用户/订单/菜品/偏好)
Redis - 缓存(推荐结果 + Session)
RabbitMQ - 消息队列(5大业务场景异步解耦)
Neo4j - 图数据库(共现关系 + 购物车推荐)
Spring Mail - 邮件发送
支付宝 SDK 4.8.73 沙箱支付
Hutool 5.8.27 工具类库
Knife4j 4.4.0 API 文档
JWT 0.12.6 Token 认证
OpenFeign - 服务间调用
Caffeine 3.1.8 本地缓存

前端技术栈

技术 版本 说明
Vue 3.4.27 前端框架
Vue Router 4.3.2 路由
Pinia 2.1.7 状态管理
Element Plus 2.7.6 UI 组件库
Axios 1.7.2 HTTP 请求
Vite 5.2.11 构建工具
Day.js 1.11.11 日期处理

功能特性

用户端

  • 用户注册 / 登录(Session + JWT 认证)
  • 菜品浏览与分类筛选
  • 购物车增删改查
  • 购物车智能搭配推荐(规则引擎 + 共现推荐双引擎)
  • 下单结算
  • 支付宝沙箱支付
  • 订单状态实时轮询(2秒/次)
  • 支付成功邮件通知
  • 个性化菜品推荐(基于行为权重的偏好推荐)
  • 全局热销推荐(用于新用户冷启动)
  • 订单评价

系统特性

  • RabbitMQ 全链路异步解耦:5 个 Exchange/Queue 覆盖行为采集、邮件通知、订单超时等核心场景
  • 订单超时自动取消:TTL + 死信队列实现 30 分钟未支付自动取消
  • MySQL 行为权重推荐引擎:浏览(0.3) → 加购(1.5) → 收藏(3.0) → 下单(8.0) 加权聚合
  • Neo4j 图共现推荐:基于用户历史订单构建菜品共现关系,实现购物车智能搭配
  • 支付兜底机制:支付宝回调丢失时,主动查询订单状态同步
  • 降级容错:MQ 不可用时自动降级为同步处理,核心业务不受影响
  • Redis 双层缓存:推荐结果缓存 + Session 持久化
  • 雪花算法:唯一订单号生成
  • 全局异常处理:统一错误响应
  • 单元测试覆盖:45+ 单元测试用例

项目结构

复制代码
take-out/                              # 后端项目根目录
├── src/
│   ├── main/
│   │   ├── java/org/example/
│   │   │   ├── common/                # 通用类
│   │   │   │   ├── context/           # 用户上下文
│   │   │   │   ├── exception/         # 全局异常处理
│   │   │   │   ├── result/            # 响应结果枚举
│   │   │   │   ├── util/
│   │   │   │   │   ├── BehaviorWeight.java   # 行为权重常量
│   │   │   │   │   └── SnowflakeIdWorker.java # 雪花ID生成
│   │   │   │   └── R.java             # 统一响应封装
│   │   │   ├── config/                 # 配置类
│   │   │   │   ├── RabbitMQConfig.java       # MQ 5个Exchange/Queue定义
│   │   │   │   ├── RabbitTemplateConfig.java # MQ序列化配置
│   │   │   │   ├── RedisConfig.java          # Redis配置
│   │   │   │   ├── SecurityConfig.java       # 安全配置
│   │   │   │   └── ...
│   │   │   ├── controller/             # 控制层
│   │   │   │   ├── RecommendController.java     # MySQL推荐接口
│   │   │   │   ├── CartRecommendController.java # Neo4j购物车推荐
│   │   │   │   ├── BehaviorController.java     # 行为上报接口
│   │   │   │   ├── Neo4jSyncController.java    # 图谱同步接口
│   │   │   │   ├── OrderController.java        # 订单控制器
│   │   │   │   ├── PayController.java          # 支付控制器
│   │   │   │   └── ...
│   │   │   ├── dto/                    # 数据传输对象
│   │   │   │   ├── RecommendFoodVO.java        # 推荐菜品VO
│   │   │   │   ├── CartRecommendRequest.java   # 购物车推荐请求
│   │   │   │   ├── CartRecommendResponse.java  # 购物车推荐响应
│   │   │   │   └── ...
│   │   │   ├── entity/                 # 实体类
│   │   │   │   ├── UserBehaviorLog.java        # 行为日志实体
│   │   │   │   ├── UserFoodPreference.java     # 用户偏好实体
│   │   │   │   ├── Orders.java / OrderItem.java
│   │   │   │   └── ...
│   │   │   ├── listener/               # MQ 消费者
│   │   │   │   ├── UserBehaviorConsumer.java   # 行为消费→偏好更新
│   │   │   │   ├── OrderPayConsumer.java       # 支付成功邮件消费
│   │   │   │   ├── OrderCancelConsumer.java    # 超时订单取消消费
│   │   │   │   └── CodeMailConsumer.java       # 验证码邮件消费
│   │   │   ├── mapper/                 # MyBatis Mapper
│   │   │   │   ├── ResFoodMapper.java          # 推荐核心SQL
│   │   │   │   └── ...
│   │   │   ├── mq/                     # MQ 消息定义
│   │   │   │   ├── consumer/
│   │   │   │   │   └── UserBehaviorConsumer.java
│   │   │   │   └── dto/
│   │   │   │       ├── BehaviorMessage.java    # 行为消息
│   │   │   │       ├── OrderPayMessage.java    # 支付消息
│   │   │   │       ├── OrderDelayMessage.java  # 延迟消息
│   │   │   │       └── CodeMailMessage.java    # 验证码消息
│   │   │   ├── service/                # 业务层
│   │   │   │   ├── RecommendService.java       # MySQL推荐接口
│   │   │   │   ├── CartRecommendService.java   # Neo4j购物车推荐接口
│   │   │   │   ├── BehaviorService.java        # 行为采集接口
│   │   │   │   ├── impl/
│   │   │   │   │   ├── RecommendServiceImpl.java    # MySQL推荐实现
│   │   │   │   │   ├── CartRecommendServiceImpl.java # Neo4j推荐实现
│   │   │   │   │   ├── BehaviorServiceImpl.java      # 行为采集实现
│   │   │   │   │   └── ...
│   │   │   │   └── ...
│   │   │   ├── takeout/                # Neo4j 专用
│   │   │   │   ├── config/
│   │   │   │   │   └── Neo4jDataInitializer.java   # 图谱初始化
│   │   │   │   └── neo4j/
│   │   │   │       ├── entity/
│   │   │   │       │   ├── FoodNode.java           # 菜品节点
│   │   │   │       │   ├── UserNode.java           # 用户节点
│   │   │   │       │   └── OrderedRelation.java    # 下单关系
│   │   │   │       └── repository/
│   │   │   │           ├── FoodNodeRepository.java
│   │   │   │           └── UserNodeRepository.java
│   │   │   └── TakeOutApplication.java  # 启动类
│   │   └── resources/
│   │       ├── application.yml              # 主配置(含MQ/Neo4j配置)
│   │       ├── application-dev.yml          # 开发环境配置
│   │       ├── application-prod.yml         # 生产环境配置
│   │       └── mapper/
│   │           └── ResFoodMapper.xml        # 推荐核心SQL映射
│   └── test/
│       └── java/org/example/
│           ├── service/impl/
│           │   └── MailServiceImplTest.java
│           ├── mq/
│           │   └── OrderNotifyProducerTest.java
│           └── MailIntegrationTest.java
└── pom.xml

快速开始

环境要求

  • JDK 17+
  • Node.js 18+
  • MySQL 8.x
  • Redis 6.x+
  • RabbitMQ 3.x+
  • Neo4j 5.x+

1. 数据库准备

创建数据库并导入表结构:

ruby 复制代码
CREATE DATABASE IF NOT EXISTS res DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE res;
-- 执行建表脚本(项目中提供的 SQL 文件)

核心数据表结构(推荐相关):

ruby 复制代码
-- 用户行为日志表
CREATE TABLE user_behavior_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id VARCHAR(32) NOT NULL,
    food_id BIGINT NOT NULL,
    behavior_type INT NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_food (user_id, food_id)
);
-- 用户菜品偏好分表
CREATE TABLE user_food_preference (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id VARCHAR(32) NOT NULL,
food_id BIGINT NOT NULL,
score DECIMAL(10,2) DEFAULT 0,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_deleted TINYINT DEFAULT 0,
UNIQUE KEY uk_user_food (user_id, food_id, is_deleted)
);

2. 配置文件

修改 src/main/resources/application-dev.yml

ruby 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/res?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: your_password
data:
redis:
host: localhost
port: 6379
password:
database: 0
rabbitmq:
host: 127.0.0.1
port: 5672
username: admin
password: 123456
virtual-host: /
neo4j:
uri: bolt://127.0.0.1:7687
authentication:
username: neo4j
password: your_neo4j_password
mail:
host: smtp.163.com
port: 465
username: your_email@163.com
password: your_email_auth_code
alipay:
app-id: your_alipay_app_id
merchant-private-key: your_private_key
alipay-public-key: your_public_key
notify-url: your_cpolar_domain/pay/notify.action

3. 启动后端

ruby 复制代码
cd take-out
mvn clean install -DskipTests
mvn spring-boot:run

后端服务启动后访问:

  • API 地址:http://localhost:8080
  • API 文档:http://localhost:8080/doc.html
  • 推荐接口:GET /recommend/listGET /recommend/hot
  • 购物车推荐:POST /cart-recommend/recommend
  • 行为上报:POST /behavior/add
  • 图谱同步:POST /sync/neo4j/all

4. 启动前端

ruby 复制代码
cd take-out-web
npm install
npm run dev

核心业务流程

用户行为驱动推荐闭环

ruby 复制代码
┌──────────────────────────────────────────────────────────────────────┐
│                     行为驱动推荐闭环                                  │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ① 用户浏览菜品 → 前端上报 behaviorType=1(浏览)                     │
│     ↓                                                                │
│  ② BehaviorController → BehaviorServiceImpl.reportBehavior()       │
│     ↓                                                                │
│  ③ 投递 MQ → behavior_exchange → user_behavior_queue                │
│     ↓                                                                │
│  ④ UserBehaviorConsumer 消费:                                      │
│        - 写入 user_behavior_log (行为日志)                          │
│        - 更新 user_food_preference (偏好分 += 0.3)                  │
│        - 清除 Redis 推荐缓存                                        │
│     ↓                                                                │
│  ⑤ 用户请求推荐 → RecommendController.list()                        │
│     ↓                                                                │
│  ⑥ 缓存未命中 → 查询 MySQL (LEFT JOIN preference 表)                │
│     ↓                                                                │
│  ⑦ 返回最新偏好分排序的推荐列表 → 写入缓存                          │
│                                                                      │
│  💡 每一次用户行为都会实时更新推荐结果!                             │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

下单支付全流程

ruby 复制代码
用户选菜 → 加入购物车 → 购物车推荐搭配(Neo4j 双引擎)
    ↓
提交订单 → 投递 MQ 延迟消息(30min TTL)
    ↓
调用支付宝下单接口 → 支付
    ↓
支付宝异步回调 notify.action → 更新订单状态
    ↓
投递 MQ 支付消息 → OrderPayConsumer → 发送支付成功邮件
    ↓
前端轮询订单状态 → 自动更新
    ↓
30分钟内未支付 → 死信队列触发 → OrderCancelConsumer 自动取消

邮件通知链路

ruby 复制代码
登录/注册 → 投递 login_code_mail 队列 → CodeMailConsumer → 发送验证码邮件
    ↓
支付成功 → 投递 order_pay_queue → OrderPayConsumer → 发送支付成功HTML邮件
    ↓
MQ 投递失败 → 降级为同步发送 → 保证邮件必达

测试

单元测试

ruby 复制代码
# 运行所有单元测试
mvn test
仅运行邮件服务测试
mvn test -Dtest="MailServiceImplTest"
仅运行 MQ 生产者测试
mvn test -Dtest="OrderNotifyProducerTest"

邮件测试接口

接口 说明
GET /mail/test/config 查看邮件服务状态
GET /mail/test/simple 发送简单文本邮件
GET /mail/test/html 发送 HTML 邮件
GET /mail/test/order 发送模拟订单邮件
GET /mail/test/realOrder?orderNo=xxx 发送真实订单邮件
GET /mail/test/mq 测试 MQ 链路

推荐接口测试

ruby 复制代码
# 个性化推荐(userId 可从响应中获取)
curl "http://localhost:8080/recommend/list?userId=1&size=4"
全局热销推荐
curl "http://localhost:8080/recommend/hot?limit=10"
购物车双引擎推荐
curl -X POST "http://localhost:8080/cart-recommend/recommend" 

-H "Content-Type: application/json" 

-d '{"userId":"1","cartItems":[{"fid":1,"num":2},{"fid":3,"num":1}]}'
触发图谱全量同步
curl -X POST "http://localhost:8080/sync/neo4j/all"
手动重建共现关系
curl -X POST "http://localhost:8080/cart-recommend/sync-co-occurrence"
上报用户行为(异步入MQ)
curl -X POST "http://localhost:8080/behavior/add" 

-H "Content-Type: application/json" 

-d '{"userId":"1","foodId":5,"behaviorType":4}'

关键设计说明

RabbitMQ 消息可靠性保障

机制 实现 说明
发布确认 publisher-confirm-type: correlated 消息成功到达 Broker 后回调确认
消息返回 publisher-returns: true 消息无法路由时返回生产者
自动重队列 default-requeue-rejected: false 拒绝消息不自动重入队列,避免无限循环
消费确认 acknowledge-mode: auto 自动确认,简化消费端逻辑
TTL + 死信 延迟队列设置 x-message-ttl + x-dead-letter-exchange 实现订单超时自动取消

MySQL 推荐缓存策略

  • 缓存 Keyrecommend:v2:food:user:{userId}(个性化)、recommend:hot(热销)
  • TTL:2 小时(推荐数据不需要实时更新)
  • 缓存更新:用户行为上报后自动清除缓存,下次请求重新查库
  • 缓存保护:所有 Redis 操作 try-catch,缓存异常不影响主流程

Neo4j 推荐降级策略

  • Neo4j 连接失败时,CartRecommendServiceImpl 返回 null,前端不显示推荐窗口
  • 共现查询无结果时,降级为按品类热销推荐
  • 启动时 Neo4j 不可用不影响 MySQL 推荐和其他核心业务

支付兜底机制

由于支付宝回调可能因网络原因丢失(如 cpolar 内网穿透不稳定),系统实现了主动兜底逻辑:

  1. 在支付回调接口中,下单后立即查询支付宝订单状态
  2. 在订单状态轮询接口中,主动查询支付宝获取订单真实状态
  3. 若发现已支付,立即更新本地订单并触发邮件通知

常见问题

Q: 启动时 RabbitMQ 连接失败怎么办?

RabbitMQ 连接失败时,系统会自动降级,不影响核心业务。但以下功能将不可用:

  • 异步邮件通知(降级为同步发送)
  • 订单超时自动取消
  • 行为异步采集(降级为同步处理)

建议:

  • 确保 RabbitMQ 服务正常运行
  • 检查 application.ymlapplication-dev.yml 中的 RabbitMQ 配置
  • 或暂时移除 spring-boot-starter-amqp 依赖

Q: Neo4j 连接失败怎么办?

Neo4j 连接失败时:

  • MySQL 推荐(/recommend/list/recommend/hot)不受影响
  • 购物车推荐(/cart-recommend/recommend)将返回空数据
  • 系统启动时 Neo4j 数据初始化会跳过,不阻塞启动

Q: 如何初始化 Neo4j 图谱数据?

有两种方式:

  1. 自动初始化 :项目启动时 Neo4jDataInitializer 会自动执行全量同步
  2. 手动同步 :调用 POST /sync/neo4j/all 接口触发全量同步

Q: 如何更新菜品共现关系?

共现关系 (OFTEN_BOUGHT_WITH) 是基于历史订单预计算的静态关系,需要手动触发更新:

  • 调用 POST /cart-recommend/sync-co-occurrence 接口重建
  • 或重新启动项目触发 Neo4jDataInitializer

Q: 推荐缓存如何清除?

有三种方式:

  1. 用户行为上报时自动清除对应用户的推荐缓存
  2. 缓存 2 小时 TTL 到期后自动失效
  3. 可通过 Redis 命令手动删除 recommend:v2:food:user:*

项目地址:hac88406-gif/Takeout

相关推荐
常宇佳1 小时前
vue3 $refs使用方法
前端·vue.js·typescript
伟大的兔神10 小时前
我做了一个本地优先的 AI 图片工作台:Loomora v1.0.0 正式发布
前端·javascript·vue.js
Flynt13 小时前
我扒了Fantastic-admin 6.0的源码,聊聊"AI时代版本答案"这个称号
vue.js·ai编程·cursor
码艺-Alimjan15 小时前
Vue项目源码备份最佳实践:无视node_modules,打包体积仅2MB
前端·javascript·vue.js
风月说与山鬼16 小时前
十二、Vue插件
前端·vue.js
星空19 小时前
Springboot复习
java·spring boot·spring
老郑聊AI业财智造20 小时前
TOGAF、DDD与微服务:软件架构的多维透视
spring boot·spring cloud·微服务·金融·架构·软件工程·制造
wno70421 小时前
Spring Boot JdbcTemplate配置Druid多数据源
java·spring boot·后端
wear工程师1 天前
Spring Boot 服务挂了却没重启?先分清 systemd 的三次判断
spring boot