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

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

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

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

相关推荐
修行者Java2 小时前
(七)从 “非结构化数据难存储” 到 “MongoDB 灵活赋能”——MongoDB 实战进阶指南
数据库·mongodb
野犬寒鸦2 小时前
TCP协议核心:TCP详细图解及TCP与UDP核心区别对比(附实战解析)
服务器·网络·数据库·后端·面试
江一破2 小时前
InfluxDB 详细介绍
数据库·influxdb
草莓熊Lotso2 小时前
MySQL 数据库基础入门:从概念到实战
linux·运维·服务器·数据库·c++·人工智能·mysql
mingdong06082 小时前
MySQL 的mysql_secure_installation安全脚本执行过程介绍
数据库·mysql·安全
小小unicorn2 小时前
[微服务即时通讯系统]3.服务端-环境搭建
数据库·c++·redis·微服务·云原生·架构
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 基于springBoot微服务架构的老年人社交系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
admin and root2 小时前
记一次攻防演练redis未授权访问案例
网络·数据库·redis·安全·web安全·渗透测试·src漏洞挖掘