苍穹外卖:菜品新增功能全流程解析

🔥个人主页:北极的代码(欢迎来访)

🎬作者简介:java后端学习者

❄️个人专栏:苍穹外卖日记,SSM框架深入,JavaWeb

✨命运的结局尽可永在,不屈的挑战却不可须臾或缺!

前言:前面我们讲解了新增菜品的前面的一部分,主要是文件的上传原理及其具体操作步骤,这里我们具体详解新增菜品的业务操作及其业务难点。

功能流程概览:

详细流程的代码实现:

Controller层:

复制代码
@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();
    }
}

作用:

  • ✅ 接收前端请求(接电话)

  • ✅ 参数校验(确认菜单没写错)

  • ✅ 调用Service层(喊厨师长过来)

  • ✅ 返回结果(告诉顾客收到了)

形象比喻:就像餐厅门口接待员,接过顾客的菜单,喊一声"后厨接单",然后告诉顾客"好的,稍等"。

DTO类 (DishDTO.java)

复制代码
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<>(); }

Service层 (DishServiceImpl.java)

复制代码
  @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);

作用:

  • ✅ 核心业务逻辑(怎么搭配菜品)

  • ✅ 事务管理(要么全部成功,要么全部失败)

  • ✅ 数据处理(DTO转成Entity)

  • ✅ 调用Mapper(指挥厨师干活)

  • 形象比喻 :就像厨师长,研究菜品做法,安排厨师炒菜,还要确保主菜和配菜能完美搭配。

Mapper层 (DishMapper.java)

@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); }

作用:

  • ✅ 执行SQL(真的去炒菜)

  • ✅ 主键返回(给菜品贴编号)

  • ✅ 操作数据库(和冰箱打交道)

形象比喻:就像真正的厨师,负责具体炒菜、装盘、贴标签。

口味Mapper (DishFlavorMapper.java)

@Mapper public interface DishFlavorMapper { /** * 批量插入口味数据 * @param flavors 口味列表 */ void insertBatch(List<DishFlavor> flavors); }

数据库相关操作:

  • ✅ 持久化存储(保存食材)

  • ✅ 数据关联(主菜和调料对应)

  • ✅ 事务支持(保证数据完整)

形象比喻 :就像后厨的大冰箱,所有食材都存放在这里,需要时取出,用完放回。

关键点说明

事务管理 @Transactional
复制代码
@Transactional public void saveWithFlavor(DishDTO dishDTO) {// 如果口味保存失败,菜品数据也会回滚 // 保证数据一致性:要么全部成功,要么全部失败 }
主键返回 @Options

为什么需要主键返回,因为。dishId 是数据库自动生成的主键。先保存菜品,数据库生成ID

,再用这个ID关联口味等其他信息,前端在新增时不传递 dishId

复制代码
@Options(useGeneratedKeys = true, keyProperty = "id")
// 作用:插入后自动将数据库生成的ID设置到dish对象的id属性
ThreadLocal保存用户信息
复制代码
BaseContext.getCurrentId() // 作用:在多线程环境下,每个线程都能获取到自己的登录用户ID

类比理解

现实世界 程序世界
你看菜单、跟服务员沟通 前端页面(用户界面)
服务员记录你的需求 Controller层(接收请求)
厨师长研究怎么做 Service层(业务逻辑)
厨师具体炒菜 Mapper层(数据操作)
冰箱里存放食材 数据库(数据存储)
最后菜端到你桌上 返回结果
层级 角色 职责 生活中的例子
前端 点菜员 收集用户输入,展示结果 拿着菜单让你勾选的人
Controller 接待员 接收请求,调用服务,返回结果 门口接单喊"后厨接单"的人
Service 厨师长 业务逻辑,事务管理 研究菜品做法,安排工作的人
Mapper 厨师 操作数据库,执行SQL 真正炒菜、放冰箱的人
DB 冰箱 存储数据,保证持久化 存放食材的大冰箱

结语:如果对你有一点点的帮助,请**点赞,关注,收藏,**你的支持就是我最大的鼓励!

相关推荐
JuiceFS2 分钟前
从 Meta 的工程实践看 EB 级 AI 存储架构
人工智能·后端
数据库小学妹6 分钟前
MySQL NULL陷阱:NOT IN、三值逻辑与NOT EXISTS
数据库·后端·mysql
AI入职记10 分钟前
10 万个会话吃掉 1.1G 内存:Spring AI 会话记忆的两个隐藏成本
后端
SelectDB10 分钟前
Doris 异步物化视图实战:与 Snowflake MV / Dynamic Tables 分层建模对照
大数据·数据库·云原生
枫昕柚11 分钟前
MySQL 建库命令详解:从 CREATE DATABASE 到create table
数据库·mysql
小兔子20 分钟前
getenv 返回的是别人的内存,改一下会改变整个进程的环境
后端
程序员cxuan25 分钟前
通了个宵,终于把 Super-Agent 架构玩明白了
人工智能·后端·程序员
不积跬步无以至千里mmbb2627 分钟前
rust_analysis
开发语言·后端·rust
张张123y35 分钟前
后端第十六课:Multi-Agent 多智能体(让多个 Agent 分工协作)
大数据·数据库·人工智能·python·deepseek·dsh·agent 框架
todoitbo1 小时前
MongoDB迁移之后,Spring Data 和 Hibernate 各自接哪类请求
大数据·数据库·mongodb·spring·hibernate·金仓数据库