您现在的位置是:首页 >学无止境 >瑞吉外卖 - 新增菜品功能(16)网站首页学无止境

瑞吉外卖 - 新增菜品功能(16)

javgo.cn 2024-06-17 11:27:04
简介瑞吉外卖 - 新增菜品功能(16)

某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 + JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。
相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PDe_7uIg?pwd=x548
提取码:x548

1.需求分析

后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。

2.数据模型

新增菜品,其实就是将新增页面录入的菜品信息插入到 dish 表,如果添加了口味做法,还需要向 dish_flavor 表插入数据。所以在新增菜品时,涉及以下两个表:

dish:菜品表

dish_flavor:菜品口味表

3.代码开发

3.1 搭建代码框架

在开发业务功能前,先将需要用到的类和接口基本结构创建好,流程与之前一样。

实体类 DishDishFlavor 直接从下载资料中导入即可,Dish 实体前面已经导入过了,DishFlavor 实体类完整代码如下:

package cn.javgo.reggie_take_out.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 菜品口味
 */
@Data
public class DishFlavor implements Serializable {
    // 序列化版本号
    private static final long serialVersionUID = 1L;
    
    //主键
    private Long id;
    
    //菜品id
    private Long dishId;
    
    //口味名称
    private String name;
    
    //口味数据 list
    private String value;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
    
    //是否删除
    private Integer isDeleted;
}

Mapper 接口 DishFlavorMapper 相关代码如下:

package cn.javgo.reggie_take_out.mapper;

import cn.javgo.reggie_take_out.entity.DishFlavor;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor> {
}

业务层接口 DishFlavorService 相关代码如下:

package cn.javgo.reggie_take_out.service;

import cn.javgo.reggie_take_out.entity.DishFlavor;
import com.baomidou.mybatisplus.extension.service.IService;

public interface DishFlavorService extends IService<DishFlavor> {
}

业务层实现类 DishFlavorServicelmpl 相关代码如下:

package cn.javgo.reggie_take_out.service.impl;

import cn.javgo.reggie_take_out.entity.DishFlavor;
import cn.javgo.reggie_take_out.mapper.DishFlavorMapper;
import cn.javgo.reggie_take_out.service.DishFlavorService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

@Service
public class DishFlavorServicelmpl extends ServiceImpl<DishFlavorMapper, DishFlavor> implements DishFlavorService {
}

控制层 DishController 相关代码如下:

package cn.javgo.reggie_take_out.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 菜品控制器
 */
@RestController
@RequestMapping("/dish")
public class DishController {
    @Resource
    private DishService dishService;
    
    @Resource
    private DishFlavorService dishFlavorService;
    
    @Resource
    private CategoryService categoryService;
}

3.2 梳理交互过程

在开发代码之前,需要梳理一下新增菜品时前端页面和服务端的交互过程:

  1. 页面(backend/page/food/add.html)发送 ajax 请求,请求服务端获取菜品分类数据并展示到下拉框中;
  2. 页面发送请求进行图片上传,请求服务端将图片保存到服务器;
  3. 页面发送请求进行图片下载,将上传的图片进行回显;
  4. 点击保存按钮,发送 ajax 请求,将菜品相关数据以 json 形式提交到服务端。

开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这 4 次请求即可。

展示分类列表的请求:

上传图片的请求:(已实现)

下载图片的请求:(已实现)

保存菜品信息的请求:

3.3 代码实现

CategoryController 中对应的处理分类列表的方法如下:

@RestController
@RequestMapping("/category")
public class CategoryController {
    /**
     * 根据条件查询分类列表
     * @param category 分类实体
     * @return R
     */
    @GetMapping("/list")
    public R<List<Category>> list(Category category){
        // 1.构造查询条件
        QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
        // 2.添加查询条件(根据分类类型查询)
        queryWrapper.eq("type",category.getType());
        // 3.添加排序条件(先根据 sort 字段进行排序,再根据更新时间进行排序)
        queryWrapper.orderByAsc("sort").orderByDesc("update_time");
        // 4.执行查询
        List<Category> list = categoryService.list(queryWrapper);
        // 5.返回结果
        return R.success(list);
    }
    
    // 省略其他方法
}

在开发处理保存菜品信息方法是我们需要先关注一个问题,方法参数直接封装为 Dish 吗?不妨我们先来看看在发送请求时都携带了哪些 json 数据:

从其中圈出的口味可以看出 json 数据中还携带了关于口味的信息,但是该属性在 Dish 实体类里面是没有的。这种情况下我们可以使用 DTO,全称为 Data Transfer Object,即数据传输对象,一般用于显示层与服务层之间的数据传输。对此我们可以提供一个 dto/DishDto.java 用于封装页面提交的数据。

资料位置:瑞吉外卖瑞吉外卖项目资料dto

该目录下有三个 Dto 类,我们可以一次全部复制到项目的 dto 目录下,以免后续重复导入。

完整代码如下:

package cn.javgo.reggie_take_out.dto;

import cn.javgo.reggie_take_out.entity.Dish;
import cn.javgo.reggie_take_out.entity.DishFlavor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;

@Data
public class DishDto extends Dish {
    // 菜品口味,接收前端传来的数据
    private List<DishFlavor> flavors = new ArrayList<>();
    // 菜品分类名称
    private String categoryName;

    private Integer copies;
}

其中的 flavors 属性就是用来处理请求携带的 json 数据中的 flavors 口味数据的,同时该类继承了 Dish 实体类,拥有 Dish 的属性。这样我们就可以在相应的处理方法中提供 DishDto 作为方法参数进行封装了。

DishController 中对应的处理保存菜品信息方法如下:

@RestController
@RequestMapping("/dish")
public class DishController {
    @Resource
    private DishService dishService;

    /**
     * 新增菜品
     * @param dishDto 自定义的 DTO 对象,用于接收前端传递的数据
     * @return R
     */
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        // 调用菜品业务层自定义的新增菜品方法
        dishService.saveWithFlavor(dishDto);
        return R.success("新增菜品成功");
    }
    
    // 省略其他方法
}

由于保存操作要同时操作 dish 和 dish_flavor 表两张表,因此我们需要自定义 saveWithFlavor 保存方法,DishService 中对应的抽象方法如下:

package cn.javgo.reggie_take_out.service;

import cn.javgo.reggie_take_out.dto.DishDto;
import cn.javgo.reggie_take_out.entity.Dish;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * @author: JavGo
 * @description: TODO
 * @date: 2023/5/17 20:38
 */
public interface DishService extends IService<Dish> {
    /**
     * 新增菜品,同时新增菜品口味(需要操作两张表)
     * @param dishDto 自定义的 DTO 对象,用于接收前端传递的数据
     */
    public void saveWithFlavor(DishDto dishDto);
}

然后在 DishServiceImpl 中进行具体实现,完整代码如下:

package cn.javgo.reggie_take_out.service.impl;

import cn.javgo.reggie_take_out.dto.DishDto;
import cn.javgo.reggie_take_out.entity.Dish;
import cn.javgo.reggie_take_out.entity.DishFlavor;
import cn.javgo.reggie_take_out.mapper.DishMapper;
import cn.javgo.reggie_take_out.service.DishFlavorService;
import cn.javgo.reggie_take_out.service.DishService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

/**
 * @author: JavGo
 * @description: TODO
 * @date: 2023/5/17 20:39
 */
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Resource
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品,同时新增菜品口味(需要操作两张表)
     * @param dishDto 自定义的 DTO 对象,用于接收前端传递的数据
     */
    @Transactional
    @Override
    public void saveWithFlavor(DishDto dishDto) {
        // 1.保存菜品的基本信息到菜品表
        this.save(dishDto);
        // 2.获取菜品的 id
        Long dishId = dishDto.getId();
        // 3.获取菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        // 4.遍历菜品口味,设置菜品 id
        flavors.forEach(flavor -> flavor.setDishId(dishId));
        // 5.批量保存菜品口味
        dishFlavorService.saveBatch(flavors);
    }
}

4.功能测试

重启应用,进行新建菜品功能测试:

查看数据库,添加成功:

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。