视频加载失败

苍穹外卖 Day03:菜品 CRUD、店铺状态与微信登录

718 字
4 分钟
苍穹外卖 Day03:菜品 CRUD、店铺状态与微信登录

菜品模块完整 CRUD#

接口一览(/admin/dish)#

方法路径说明
POST/admin/dish新增菜品(含口味,前一日已完成)
GET/admin/dish/page分页查询(可选条件:name/categoryId/status)
DELETE/admin/dish?ids=1,2,3批量删除(删除前校验)
GET/admin/dish/{id}根据 id 查询(含口味,回显用)
PUT/admin/dish修改菜品(先删口味再重插)

分页查询#

PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
return new PageResult(page.getTotal(), page.getResult());

DishMapper.xml 里 left outer join category c on d.category_id = c.id 联表取 categoryName,配合 <where> 动态条件与 order by d.create_time desc 排序。

批量删除#

删除前做两道校验,全部通过才执行删除:

// 校验是否起售中
for (Long id : ids) {
Dish dish = dishMapper.getById(id);
if (dish.getStatus() == StatusConstant.ENABLE) {
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
// 校验是否被套餐关联
List<Long> setmealIds = setmealDishMapper.getSetmealDishIds(ids);
if (setmealIds != null && setmealIds.size() > 0) {
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
// 批量删主表 + 批量删口味表
dishMapper.deleteByIds(ids);
dishFlavorMapper.deleteByDishIds(ids);

套餐关联校验对应的 SQL 在 SetmealDishMapper.xml:select setmeal_dish.id from setmeal_dish where setmeal_dish.dish_id in (...)。

修改菜品#

主表 update(动态 <set>)+ 删旧口味 + 批量插新口味:

dishMapper.update(dish); // 动态 set,AutoFill 补 updateTime/updateUser
dishFlavorMapper.deleteByDishId(dishDTO.getId()); // 删旧口味
// 再 insertBatch 新口味(foreach 回填 dishId)

DishMapper.xml 的 deleteByIds/update 用到 <foreach> / <set> / <if> 动态标签;公共字段由 @AutoFill(value = OperationType.INSERT/UPDATE) 标记、切面自动填充。

Redis 店铺营业状态#

营业状态是高频访问、且要被管理端与用户端跨端共享的数据,适合放 Redis。存 Integer 值:1 = 营业中,0 = 打烊中;key 为常量 SHOP_STATUS,读取走 (Integer) redisTemplate.opsForValue().get(KEY)。

RedisTemplate 配置的一个坑#

@Configuration
@Slf4j
public class RedisConfiguration {
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
// 设置了 key 的 String 序列化器
template.setKeySerializer(new StringRedisSerializer());
return template;
}
}

问题是 redisTemplate 方法上少了 @Bean:该方法不会被 Spring 调用注册,自定义序列化配置完全不生效。此时 @Autowired RedisTemplate 注入的实际是 Spring Boot 自动配置(RedisAutoConfiguration)提供的默认 RedisTemplate(JDK 序列化)。功能能跑,但 key/value 在 Redis 里是二进制乱码。修复就是在方法上加 @Bean。

admin/user 双 ShopController#

管理端与用户端各有一个 ShopController,类名相同会引发 bean 名冲突,用显式命名规避:@RestController("adminShopController") / @RestController("userShopController")。

  • 管理端:PUT /admin/shop/{status} 设置状态;GET /admin/shop/status 查询
  • 用户端:GET /user/shop/status 只读查询

微信登录#

登录流程(UserController.login → UserServiceImpl.wxLogin):

  1. 小程序调 wx.login() 拿临时 code
  2. 后端请求微信 https://api.weixin.qq.com/sns/jscode2session(参数:appid/secret/js_code/grant_type=authorization_code),换回 openid(附 session_key)
  3. 按 openid 查 user 表:不存在则自动注册(UserMapper.xml 的 insert 配 useGeneratedKeys="true" keyProperty="id" 回填主键);存在则直接登录
  4. 签发用户端 JWT(JwtProperties.userSecretKey/userTtl,claims 放 userId)
  5. 返回 UserLoginVO{id, openid, token},后续请求在请求头携带 token

关键点:

  • openid 为 null 时抛 LoginFailedException(LOGIN_FAILED),走业务异常统一处理
  • claims 键用 JwtClaimsConstant.USER_ID = "userId",与管理端的 EMP_ID 区分
  • UserMapper 上有注解 SQL @Select("select * from user where openid = #{openId}"),insert 走 XML

两个待修隐患:

  • UserServiceImpl 误 import 了 org.aspectj.bridge.Message,属无用脏 import,应删除
  • 用户端 JWT 校验拦截器尚未实现——WebMvcConfiguration 目前只注册了 /admin/** 的 JwtTokenAdminInterceptor,/user/** 的校验需要补上

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
苍穹外卖 Day03:菜品 CRUD、店铺状态与微信登录
https://ryuki.icu/posts/sky-takeout-day03/
作者
RK
发布于
2026-08-21
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
RK
Long may the sunshine
公告
Long may the sunshine
分类
标签
最新动态
站点天气
天气加载中…
站点统计
文章
27
分类
6
标签
59
总字数
39,139
篝火已燃
0 天
最后活动
0 天前
每日一签