添加购物车功能的业务逻辑可以概括为: “先查后判,有则加一,无则新增” 。
这不仅仅是一个简单的数据库插入操作,而是一个包含“查询、判断、分支处理”的完整业务流程。
1. 第一步:确认“谁”在买?(获取身份)
购物车是私有的,第一件事必须知道当前操作的用户是谁。
- 动作:后端通过
BaseContext.getCurrentId()获取当前登录用户的 ID (user_id)。 - 目的:后续所有的增删改查 SQL 语句,都必须带上
user_id = ?这个条件,实现数据隔离。
2. 第二步:他在买“什么”?(构建查询条件)
用户前端传来的 ShoppingCartDTO 包含了商品的核心标识:
- 可能是菜品:
dish_id+dish_flavor(注意:口味也是唯一标识的一部分,"微辣"和"重辣"是两条不同的购物车记录)。 - 可能是套餐:
setmeal_id。
3. 第三步:车里“有没有”?(核心判断)
这是最关键的一步。系统需要拿着上面的 user_id 和商品标识去数据库的 shopping_cart 表里查一下。
- SQL 逻辑:
SELECT * FROM shopping_cart WHERE user_id = ? AND dish_id = ? AND dish_flavor = ?(如果是套餐则查 setmeal_id)。
4. 第四步:分支处理(有则改,无则增)
根据第三步的查询结果,逻辑分为两条路:
分支 A:商品已存在(老朋友)
如果查询结果 list 不为空(size > 0),说明购物车里已经有这个商品了。
-
逻辑:不需要插入新数据,只需要把现有的数量加 1。
-
动作:
- 取出这条数据对象。
item.setNumber(item.getNumber() + 1);- 调用
shoppingCartMapper.updateNumberById(item);执行更新。
分支 B:商品不存在(新面孔)
如果查询结果为空,说明这是用户第一次要把这个商品放进购物车。
-
逻辑:需要创建一条全新的记录,初始数量为 1。
-
难点:前端传来的 DTO 里只有 ID,没有商品名称、价格、图片。数据库不能只存 ID,否则展示购物车时还得去回查其他表。
-
动作:
-
补全信息:
- 如果是菜品:调用
DishMapper根据dish_id查出菜品的名字、图片、价格。 - 如果是套餐:调用
SetmealMapper根据setmeal_id查出套餐的名字、图片、价格。
- 如果是菜品:调用
-
构建对象:创建一个新的
ShoppingCart对象,填入查到的信息,并设置user_id,设置number = 1,设置create_time。 -
插入数据库:调用
shoppingCartMapper.insert(cart);。
-
💡 核心逻辑总结图
你可以想象一个流程图:
-
请求进来 -> 2. 查数据库 (user_id + dish_id/setmeal_id + flavor)
- 结果存在 -> 数量 + 1 -> Update
- 结果不存在 -> 去查菜品/套餐表补全信息 -> 数量设为 1 -> Insert
⚠️ 需要特别注意的点
-
冗余设计:为什么购物车表里要存
name、image、amount?- 这是典型的空间换时间。如果购物车表里只有 ID,将来用户查看购物车时,后端就得把购物车表和菜品表、套餐表进行复杂的多表联查 (Join) ,性能很差。直接冗余存进去,查购物车时单表查询即可,速度极快。
-
口味的重要性:在判断“商品是否存在”时,千万别漏了
dish_flavor。- 场景:用户先加了一份“可乐(加冰)”,又加了一份“可乐(常温)”。如果不判断口味,系统会错误地把“加冰可乐”的数量变成 2,导致用户买不到常温可乐。