某马瑞吉外卖单体架构项目完整开发文档,基于 Spring Boot 2.7.11 + JDK 11。预计 5 月 20 日前更新完成,有需要的胖友记得一键三连,关注主页 “瑞吉外卖” 专栏获取最新文章。
相关资料:https://pan.baidu.com/s/1rO1Vytcp67mcw-PDe_7uIg?pwd=x548
提取码:x548
文章目录
- 1.需求分析
 - 2.代码开发
 - 2.1 执行过程分析
 - 2.2 代码实现
 - 2.2.1 准备实体类
 - 2.2.2 准备 Mapper 接口
 - 2.2.3 准备 Service 接口及实现类
 - 2.2.4 自定义 remove 方法
 
- 3.功能测试
 
1.需求分析
在分类管理页面,可以对某个分类进行删除操作。但是需要注意的是,当分类关联了菜品或者套餐时,此分类不允许删除。

2.代码开发
2.1 执行过程分析
在开发代码之前,需要梳理一下整个程序的执行过程:
- 页面发送 ajax 请求,将参数(id)提交到服务端;
 - 服务端 Controller 接收页面提交的数据并调用 Service 删除数据;
 - Service 调用 Mapper 操作数据库。
 
下面同样简单看一下对应客户端发送的请求信息:

2.2 代码实现
注意,因为当分类关联了菜品或者套餐时,此分类不允许删除。因此,我们不能简单的调用 MyBatis Plus 提供的 categoryService.removeById(id) 方法直接进行删除。而是需要检查删除的分类是否关联了菜品或者套餐,因此我们需要进行一些功能上的扩展。
要完善分类删除功能,需要先准备基础的类和接口。首先需要实体类 Dish 和 Setmeal,菜品和套餐这两个实体类在前面同意导入实体的时候已经导入了,直接使用即可。
2.2.1 准备实体类
entity/Dish.java 实体类完整代码如下:
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.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * 菜品
 */
@Data
public class Dish implements Serializable {
    // 用于序列化的版本号
    private static final long serialVersionUID = 1L;
    // 主键
    private Long id;
    //菜品名称
    private String name;
    //菜品分类id
    private Long categoryId;
    //菜品价格
    private BigDecimal price;
    //商品码
    private String code;
    //图片
    private String image;
    //描述信息
    private String description;
    //0 停售 1 起售
    private Integer status;
    //顺序
    private Integer sort;
    @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;
}
 
entity/Setmeal.java 实体类完整代码如下:
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.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * 套餐
 */
@Data
public class Setmeal implements Serializable {
    // 用于序列化的版本号
    private static final long serialVersionUID = 1L;
    // 主键
    private Long id;
    //分类id
    private Long categoryId;
    //套餐名称
    private String name;
    //套餐价格
    private BigDecimal price;
    //状态 0:停用 1:启用
    private Integer status;
    //编码
    private String code;
    //描述信息
    private String description;
    //图片
    private String image;
    @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;
}
 
2.2.2 准备 Mapper 接口
对应的 mapper/DishMapper.java 接口如下:
package cn.javgo.reggie_take_out.mapper;
import cn.javgo.reggie_take_out.entity.Dish;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
    
}
 
对应的 mapper/SetmealMapper.java 接口如下:
package cn.javgo.reggie_take_out.mapper;
import cn.javgo.reggie_take_out.entity.Setmeal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal> {
    
}
 
2.2.3 准备 Service 接口及实现类
对应的 service/DishService.java 接口如下:
package cn.javgo.reggie_take_out.service;
import cn.javgo.reggie_take_out.entity.Dish;
import com.baomidou.mybatisplus.extension.service.IService;
public interface DishService extends IService<Dish> {
    
}
 
对应的 service/impl/DishServiceImpl.java 实现类如下:
package cn.javgo.reggie_take_out.service.impl;
import cn.javgo.reggie_take_out.entity.Dish;
import cn.javgo.reggie_take_out.mapper.DishMapper;
import cn.javgo.reggie_take_out.service.DishService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    
}
 
对应的 service/SetmealService.java 接口如下:
package cn.javgo.reggie_take_out.service;
import cn.javgo.reggie_take_out.entity.Setmeal;
import com.baomidou.mybatisplus.extension.service.IService;
public interface SetmealService extends IService<Setmeal> {
}
 
对应的 service/impl/SetmealServiceImpl.java 实现类如下:
package cn.javgo.reggie_take_out.service.impl;
import cn.javgo.reggie_take_out.entity.Setmeal;
import cn.javgo.reggie_take_out.mapper.SetmealMapper;
import cn.javgo.reggie_take_out.service.SetmealService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class SetmealServiceImpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {
    
}
 
2.2.4 自定义 remove 方法
想要实现我们的检查逻辑,就不能使用 MyBatis Plus 提供的现成方法了,我们需要在 CategoryService 接口中进行自定义:
public interface CategoryService extends IService<Category> {
    /**
     * 删除分类
     * @param id 分类id
     */
    public void remove(Long id);
}
 
然后在该接口的 CategoryServiceImpl 实现中进行抽象方法的实现:
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    @Resource
    private DishService dishService;
    @Resource
    private SetmealService setmealService;
    /**
     * 根据分类id删除分类,在删除之前判断是否关联了菜品和套餐,如果关联了则不能删除
     * @param id 分类id
     */
    @Override
    public void remove(Long id) {
        // 1.构造查询条件
        QueryWrapper<Dish> queryWrapper = new QueryWrapper<>();
        // 2.设置查询条件(根据分类id查询)
        queryWrapper.eq("category_id",id);
        // 3.查询到的菜品数量
        int count = dishService.count(queryWrapper);
        // 4.检查菜品数量,如果大于0则不能删除,抛出业务异常
        if (count > 0) {
            throw new CustomException("该分类下有菜品,不能删除");
        }
        // 5.构造查询条件
        QueryWrapper<Setmeal> queryWrapper2 = new QueryWrapper<>();
        // 6.设置查询条件(根据分类id查询)
        queryWrapper2.eq("category_id",id);
        // 7.查询到的套餐数量
        int count2 = setmealService.count(queryWrapper2);
        // 8.检查套餐数量,如果大于0则不能删除,抛出业务异常
        if (count2 > 0) {
            throw new CustomException("该分类下有套餐,不能删除");
        }
        // 9.删除分类
        baseMapper.deleteById(id);
    }
}
 
上述代码中涉及到的 common/CustomException.java 自定义业务异常如下:
package cn.javgo.reggie_take_out.common;
/**
 * 自定义业务异常
 */
public class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}
 
同时还需要在我们的 handler/GlobalExceptionHandler.java 全局异常处理器中进行该自定义异常的处理:
@ControllerAdvice(annotations = {RestController.class, Conditional.class})
@ResponseBody
public class GlobalExceptionHandler {
    /**
     * 处理自定义异常 CustomException
     * @param ex 异常对象
     * @return  响应对象
     */
    @ExceptionHandler(CustomException.class)
    public R<String> exceptionHandler(CustomException ex){
        return R.error(ex.getMessage());
    }
    
    // 省略其他方法
}
 
最后在 CategoryController 中编写对应的处理方法即可:
@RestController
@RequestMapping("/category")
public class CategoryController {
    @Resource
    private CategoryService categoryService;
    
    /**
     * 删除分类
     * @param id 分类id
     * @return R
     */
    @DeleteMapping
    public R<String> delete(Long id){
        categoryService.remove(id);
        return R.success("删除分类成功");
    }
    
    // 省略其他方法
}
 
3.功能测试
随便删除一个带有关联的分类进行删除操作:

新建一个没有关联的菜品分类:

删除新增的测试菜品,删除成功:















