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

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

🎬作者简介: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 冰箱 存储数据,保证持久化 存放食材的大冰箱

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

相关推荐
zh1570235 小时前
JavaScript中WorkerThreads解决服务端计算瓶颈
jvm·数据库·python
代码AI弗森5 小时前
一文理清楚“算力申请 / 成本测算 / 并发评估”
java·服务器·数据库
前端一小卒6 小时前
我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸
前端·javascript·后端
摇滚侠6 小时前
expdp 查看帮助
java·数据库·oracle
流年似水~6 小时前
MCP协议实战:从零搭建一个让Claude能“看见“数据库的工具服务
数据库·人工智能·程序人生·ai·ai编程
2401_871492856 小时前
Vue.js监听器watch利用回调函数处理级联下拉框数据联动
jvm·数据库·python
志栋智能7 小时前
超自动化安全:构建智能安全运营的核心引擎
大数据·运维·服务器·数据库·安全·自动化·产品运营
曹牧7 小时前
Spring:@RequestMapping注解,匹配的顺序与上下文无关
java·后端·spring
zhoutongsheng8 小时前
C#怎么实现Swagger文档 C#如何在ASP.NET Core中集成Swagger自动生成API文档【框架】
jvm·数据库·python
WinterKay8 小时前
【开源】我写了一个轻量级本地数据库浏览工具,支持 MySQL/Redis 只读查询
数据库·mysql·开源