

@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品管理接口")
@Slf4j
public class DishController {
@Autowired
private DishService dishService;
/**
* 新增菜品
* @param dishDTO 前端传来的菜品数据
* @return 操作结果
*/
@PostMapping
@ApiOperation("新增菜品")
public Result save(@RequestBody DishDTO dishDTO) {
log.info("接收到新增菜品请求:{}", dishDTO
// 调用service层处理业务
dishService.saveWithFlavor(dishDTO);
log.info("菜品新增成功");
return Result.success();
}
}形象比喻:就像餐厅门口接待员,接过顾客的菜单,喊一声"后厨接单",然后告诉顾客"好的,稍等"。
package com.sky.dto; import com.sky.entity.DishFlavor; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * 菜品数据传输对象 * 用于接收前端传递的菜品数据 */ @Data public class DishDTO implements Serializable { private Long id; // 菜品ID(新增时为null) private String name; // 菜品名称 private Long categoryId; // 分类ID private BigDecimal price; // 价格 private String image; // 图片路径 private String description; // 描述信息 private Integer status; // 状态 0:停用 1:启用 // 口味列表 private List<DishFlavor> flavors = new ArrayList<>(); } @Autowired private DishMapper dishMapper;
@Autowired private DishFlavorMapper dishFlavorMapper;
/** * 新增菜品和对应的口味
* @param dishDTO 菜品数据 */
@Override @Transactional // 事务管理,保证数据一致性
public void saveWithFlavor(DishDTO dishDTO)
{ // ========== 第一步:准备菜品数据 ========== Dish dish = new Dish(); BeanUtils.copyProperties(dishDTO, dish); // 属性拷贝
//设置公共字段
LocalDateTime now = LocalDateTime.now(); Long currentId = BaseContext.getCurrentId(); // 获取当前登录用户
ID dish.setCreateTime(now);
// 创建时间 dish.setUpdateTime(now);
// 更新时间 dish.setCreateUser(currentId);
// 创建人 dish.setUpdateUser(currentId);
// 修改人 dish.setStatus(1); // 默认为启用状态
// ========== 第二步:保存菜品基本信息 ========== log.info("保存菜品基本信息:{}", dish); dishMapper.insert(dish); // 获取数据库生成的菜品ID Long dishId = dish.getId(); log.info("菜品保存成功,生成的ID:{}", dishId); // ========== 第三步:保存口味信息 ========== List<DishFlavor> flavors = dishDTO.getFlavors(); if (flavors != null && !flavors.isEmpty()) { // 为每个口味设置菜品ID flavors.forEach(flavor -> { flavor.setDishId(dishId); }); // 批量保存口味 log.info("保存口味信息:{}", flavors); dishFlavorMapper.insertBatch(flavors);@Mapper public interface DishMapper { /** * 插入菜品数据 * @param dish 菜品对象 */ @Insert("insert into dish(name, category_id, price, image, description, " + "status, create_time, update_time, create_user, update_user) " + "values(#{name}, #{categoryId}, #{price}, #{image}, #{description}, " + "#{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})") @Options(useGeneratedKeys = true, keyProperty = "id") // 主键返回配置 void insert(Dish dish); }
形象比喻:就像真正的厨师,负责具体炒菜、装盘、贴标签。
@Mapper public interface DishFlavorMapper { /** * 批量插入口味数据 * @param flavors 口味列表 */ void insertBatch(List<DishFlavor> flavors); }
形象比喻:就像后厨的大冰箱,所有食材都存放在这里,需要时取出,用完放回。
@Transactional public void saveWithFlavor(DishDTO dishDTO) { // 如果口味保存失败,菜品数据也会回滚 // 保证数据一致性:要么全部成功,要么全部失败 }为什么需要主键返回,因为。dishId 是数据库自动生成的主键。先保存菜品,数据库生成ID
,再用这个ID关联口味等其他信息,前端在新增时不传递 dishId
@Options(useGeneratedKeys = true, keyProperty = "id")
// 作用:插入后自动将数据库生成的ID设置到dish对象的id属性BaseContext.getCurrentId() // 作用:在多线程环境下,每个线程都能获取到自己的登录用户ID现实世界 | 程序世界 |
|---|---|
你看菜单、跟服务员沟通 | 前端页面(用户界面) |
服务员记录你的需求 | Controller层(接收请求) |
厨师长研究怎么做 | Service层(业务逻辑) |
厨师具体炒菜 | Mapper层(数据操作) |
冰箱里存放食材 | 数据库(数据存储) |
最后菜端到你桌上 | 返回结果 |

层级 | 角色 | 职责 | 生活中的例子 |
|---|---|---|---|
前端 | 点菜员 | 收集用户输入,展示结果 | 拿着菜单让你勾选的人 |
Controller | 接待员 | 接收请求,调用服务,返回结果 | 门口接单喊"后厨接单"的人 |
Service | 厨师长 | 业务逻辑,事务管理 | 研究菜品做法,安排工作的人 |
Mapper | 厨师 | 操作数据库,执行SQL | 真正炒菜、放冰箱的人 |
DB | 冰箱 | 存储数据,保证持久化 | 存放食材的大冰箱 |
结语:如果对你有一点点的帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!
