Day07

21 阅读3分钟

Day07 学什么

Day07 主要做两块能力建设:缓存商品(菜品/套餐) + 购物车

你最终要达到的效果:

  • 用户端展示菜品/套餐时:优先走 Redis 缓存,减数据库压力(访问更快)。
  • 用户端购物车:能 添加查看清空

推荐学习顺序

0)先把“验收点”想清楚(你写完要怎么证明它对了)

  • 缓存:第一次查某分类 有 SQL,第二次查同分类 没 SQL,Redis 里能看到 key。
  • 数据变更:后台改菜品/套餐后,再访问用户端时能重新查库并回填缓存(说明缓存被清掉了)。

(讲义也强调用 “控制台 SQL + Redis 数据变化” 来验证)


1. 缓存菜品(RedisTemplate 手写缓存)

1.1 为什么要缓存

用户端小程序菜品列表如果每次都查数据库,访问量大时 DB 压力上来,响应会慢。

1.2 缓存策略(非常重要:你要背下来)

  • 按分类缓存:每个分类一份缓存(key 通常 dish_分类id
  • 数据变更要清缓存:新增/修改/删除/起售停售后,清理对应缓存(或干脆清 dish_*

1.3 用户端 DishController:先查 Redis,没命中再查 DB 回填

  • key:dish_ + categoryId
  • redisTemplate.opsForValue().get(key) 命中直接返回
  • 未命中:查库 dishService.listWithFlavor(dish),再 set(key, list)

你写的时候只要盯住:读缓存 → 读库 → 写缓存 这条链路。

1.4 管理端 DishController:封装 cleanCache(pattern) 清理缓存

讲义提供了一个抽取方法:keys(pattern) + delete(keys),然后在新增/修改/删除/起售停售里调用。

你最容易踩的坑:

  • keys("dish_*") 在大 key 场景会阻塞 Redis(生产上要谨慎)。课程项目里可以先这样做,后面你可以自己升级成:记录分类 key、精准删除、或用消息/延迟双删等。
  • RedisTemplate 的序列化:如果你发现 Redis 里是乱码/反序列化失败,优先检查 RedisTemplate 的 key/value serializer 配置(课程基础工程一般已配好)。

2. 缓存套餐(Spring Cache 注解式缓存)

这块重点是:用注解完成缓存读写/清理,比手写更优雅。常见注解:@Cacheable / @CachePut / @CacheEvict,启动类加 @EnableCaching

2.1 实现步骤(你按清单逐条打勾)

  1. 引入 cache + redis 依赖(讲义说已实现/或给了坐标)
  2. 启动类加 @EnableCaching 开启注解缓存
  3. 用户端 SetmealController list 上加 @Cacheable(cacheNames="setmealCache", key="#categoryId")
  4. 管理端增删改/起售停售:用 @CacheEvict 清理缓存(常见:allEntries = true

你最容易卡的点:

  • key 写法:#categoryId 是方法参数;如果你写成 #setmealDTO.categoryId(保存时)要确认参数名/字段路径正确(讲义里就是这么用的)。
  • allEntries=true:简单粗暴但正确;后面可以优化为按分类精准清理。

3. 购物车(添加 / 查看 / 清空)

3.1 表结构与规则(先搞懂,再写代码)

购物车表 shopping_cart 的关键点:购物车属于用户,菜品/套餐二选一存(dish_id 或 setmeal_id),同一商品不重复插多行,只增 number

3.2 添加购物车:/user/shoppingCart/add

  • DTO:dishId / setmealId / dishFlavor(口味)

  • Controller 调 Service 的 add 方法

  • Service 核心逻辑(你写代码就照这段心智模型):

    1. 只查当前用户:shoppingCart.setUserId(BaseContext.getCurrentId())
    2. 根据 userId + dishId/setmealId + dishFlavor 查是否已有
    3. 有:number + 1 更新
    4. 无:查 dish/setmeal 填充 name/image/amount,插入新记录、number=1

你最容易踩的坑:

  • “同一商品”的判定条件要包含 口味 dishFlavor(否则不同口味会被当成同一条合并)。
  • BaseContext 里的 currentId 必须在登录/拦截器链路里正确设置(否则 userId 为空会查出全表或插错数据)。

3.3 查看购物车:/user/shoppingCart/list

Controller 直接返回 shoppingCartService.showShoppingCart();Service 按当前 userId 查列表即可。

3.4 清空购物车:/user/shoppingCart/clean

本质就是:delete from shopping_cart where user_id = ?


一口气做完的“实操清单”

  1. ✅ 用户端菜品列表:加 Redis 缓存(命中直接返回;未命中查库回填)
  2. ✅ 管理端菜品:新增/修改/删除/起售停售后清缓存(至少做到 dish_* 清理)
  3. ✅ 启动类:@EnableCaching
  4. ✅ 用户端套餐:@Cacheable
  5. ✅ 管理端套餐:@CacheEvict
  6. ✅ 购物车:add / list / clean 三个接口跑通