3.3 代码实现
在CategoryController中增加分页查询的方法,在方法中传递分页条件进行查询,并且需要对查询到的结果,安排设置的套餐顺序字段sort进行排序。
<span style="background-color:#f8f8f8"><span style="color:#333333">/** * 分页查询 * @param page * @param pageSize * @return */ @GetMapping("/page") public R<Page> page(int page,int pageSize){ //分页构造器 Page<Category> pageInfo = new Page<>(page,pageSize); //条件构造器 LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>(); //添加排序条件,根据sort进行排序 queryWrapper.orderByAsc(Category::getSort); //分页查询 categoryService.page(pageInfo,queryWrapper); return R.success(pageInfo); }</span></span>
3.4 功能测试
分页查询的代码编写完毕之后, 我们需要重新启动项目,然后登陆系统后台,点击分类管理,查询分类列表是否可以正常展示。测试过程中可以使用浏览器的监控工具查看页面和服务端的数据交互细节。
测试完毕后,大家会发现,我们查询数据库返回的类型为 1 或者 2, 但是实际展示到页面上的却是 "菜品分类" 或 "套餐分类",这一块是在前端页面中进行处理的,处理代码如下:
4. 删除分类
4.1 需求分析
在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时,此分类不允许删除。
4.2 前端页面分析
在前端页面中,点击 "删除" 按钮,就会触发定义的方法,然后往服务端发送异步请求,并传递参数id,执行删除分类操作。
删除操作的具体执行流程如下:
1). 点击删除,页面发送ajax请求,将参数(id)提交到服务端
2). 服务端Controller接收页面提交的数据并调用Service删除数据
3). Service调用Mapper操作数据库
从上述的分析中,我们可以得到请求的信息如下:
请求 | 说明 |
请求方式 | DELETE |
请求路径 | /category |
请求参数 | ?id=1395291114922618881 |
4.3 代码实现
在CategoryController中增加根据ID删除的方法,在方法中接收页面传递参数id,然后执行删除操作。
<span style="background-color:#f8f8f8"><span style="color:#333333">/** * 根据id删除分类 * @param id * @return */ @DeleteMapping public R<String> delete(Long id){ log.info("删除分类,id为:{}",id); categoryService.removeById(id); return R.success("分类信息删除成功"); }</span></span>
4.4 功能测试
基本的删除操作代码实现完毕后,重启项目,进行测试。可以通过debug断点调试进行测试,同时结合浏览器监控工具查看请求和响应的具体数据。
4.5 功能完善
4.5.1 思路分析
在上述的测试中,我们看到分类数据是可以正常删除的。但是并没有检查删除的分类是否关联了菜品或者套餐,所以我们需要进行功能完善。完善后的逻辑为:
- 根据当前分类的ID,查询该分类下是否存在菜品,如果存在,则提示错误信息
- 根据当前分类的ID,查询该分类下是否存在套餐,如果存在,则提示错误信息
- 执行正常的删除分类操作
那么在这里又涉及到我们后面要用到的两张表结构 dish(菜品表) 和 setmeal(套餐表)。具体的表结构,我们目前先了解一下:
4.5.2 准备工作
1). 准备菜品(Dish)及套餐(Setmeal)实体类(课程资料中直接拷贝)
所属包: com.itheima.reggie.entity
<span style="background-color:#f8f8f8"><span style="color:#333333">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; }</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333">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; }</span></span>
2). Mapper接口DishMapper和SetmealMapper
所属包: com.itheima.reggie.mapper
<span style="background-color:#f8f8f8"><span style="color:#333333">import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itheima.reggie.entity.Dish; import org.apache.ibatis.annotations.Mapper; @Mapper public interface DishMapper extends BaseMapper<Dish> { }</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333">import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itheima.reggie.entity.Setmeal; import org.apache.ibatis.annotations.Mapper; @Mapper public interface SetmealMapper extends BaseMapper<Setmeal> { }</span></span>
3). Service接口DishService和SetmealService
所属包: com.itheima.reggie.service
<span style="background-color:#f8f8f8"><span style="color:#333333">import com.baomidou.mybatisplus.extension.service.IService; import com.itheima.reggie.entity.Dish; public interface DishService extends IService<Dish> { }</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333">import com.baomidou.mybatisplus.extension.service.IService; import com.itheima.reggie.entity.Setmeal; public interface SetmealService extends IService<Setmeal> { }</span></span>
4). Service实现类DishServiceImpl和SetmealServiceImpl
<span style="background-color:#f8f8f8"><span style="color:#333333">import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itheima.reggie.entity.Dish; import com.itheima.reggie.mapper.DishMapper; import com.itheima.reggie.service.DishService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @Slf4j public class DishServiceImpl extends ServiceImpl<DishMapper,Dish> implements DishService { }</span></span>
<span style="background-color:#f8f8f8"><span style="color:#333333">import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itheima.reggie.entity.Setmeal; import com.itheima.reggie.mapper.SetmealMapper; import com.itheima.reggie.service.SetmealService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @Slf4j public class SetmealServiceImpl extends ServiceImpl<SetmealMapper,Setmeal> implements SetmealService { }</span></span>
4.5.3 代码实现
1). 创建自定义异常
在业务逻辑操作过程中,如果遇到一些业务参数、操作异常的情况下,我们直接抛出此异常。
所在包: com.itheima.reggie.common
<span style="background-color:#f8f8f8"><span style="color:#333333">/** * 自定义业务异常类 */ public class CustomException extends RuntimeException { public CustomException(String message){ super(message); } }</span></span>
2). 在CategoryService中扩展remove方法
<span style="background-color:#f8f8f8"><span style="color:#333333">public interface CategoryService extends IService<Category> { //根据ID删除分类 public void remove(Long id); }</span></span>
3). 在CategoryServiceImpl中实现remove方法
<span style="background-color:#f8f8f8"><span style="color:#333333">@Autowired private DishService dishService; @Autowired private SetmealService setmealService; /** * 根据id删除分类,删除之前需要进行判断 * @param id */ @Override public void remove(Long id) { //添加查询条件,根据分类id进行查询菜品数据 LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>(); dishLambdaQueryWrapper.eq(Dish::getCategoryId,id); int count1 = dishService.count(dishLambdaQueryWrapper); //如果已经关联,抛出一个业务异常 if(count1 > 0){ throw new CustomException("当前分类下关联了菜品,不能删除");//已经关联菜品,抛出一个业务异常 } //查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常 LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>(); setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id); int count2 = setmealService.count(setmealLambdaQueryWrapper); if(count2 > 0){ throw new CustomException("当前分类下关联了套餐,不能删除");//已经关联套餐,抛出一个业务异常 } //正常删除分类 super.removeById(id); }</span></span>
那么在上述的业务逻辑中,当分类下关联的有菜品或者套餐时,我们在业务代码中抛出了自定义异常,并且在异常中封装了错误提示信息,那这个错误提示信息如何提示给页面呢?
异常抛出之后,会被异常处理器捕获,我们只需要在异常处理器中捕获这一类的异常,然后给页面返回对应的提示信息即可。
4). 在GlobalExceptionHandler中处理自定义异常
在全局异常处理器中增加方法,用于捕获我们自定义的异常 CustomException
<span style="background-color:#f8f8f8"><span style="color:#333333">/** * 异常处理方法 * @return */ @ExceptionHandler(CustomException.class) public R<String> exceptionHandler(CustomException ex){ log.error(ex.getMessage()); return R.error(ex.getMessage()); }</span></span>
5). 改造CategoryController的delete方法
注释掉原有的代码,在delete方法中直接调用categoryService中我们自定义的remove方法。
<span style="background-color:#f8f8f8"><span style="color:#333333">/** * 根据id删除分类 * @param id * @return */ @DeleteMapping public R<String> delete(Long id){ log.info("删除分类,id为:{}",id); //categoryService.removeById(id); categoryService.remove(id); return R.success("分类信息删除成功"); }</span></span>
4.5.4 功能测试
功能完善的代码编写完毕之后, 我们需要重新启动项目,进入管理系统访问分类管理, 然后进行删除分类的测试,需要将所有情况都覆盖全,例如:
1). 新增一个分类,然后再直接删除,检查是否可以正常删除成功。(新增的分类时没有关联菜品和套餐的)
2). 在数据库表(dish/setmeal)中,找到一个与菜品或套餐关联的分类,然后在页面中执行删除操作,检查是否可以正常的提示出对应的错误信息。
5. 修改分类
5.1 需求分析
在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作。
5.2 前端页面分析
这里面大家会发现,修改功能我们还没有实现,但是当点击 "修改" 按钮的时候,我们并没有开发根据ID查询数据,进行页面回显的功能,但是页面的分类数据确实回显回来了。这是怎么做到的呢,我们来解析一下前端的代码实现(前端代码已经实现):
那么回显这一步的操作前端已经实现,我们就只需要开发一个方法,修改操作的方法即可。我们可以通过浏览器来抓取一下修改操作的请求信息,如图:
具体的请求信息,整理如下:
请求 | 说明 |
请求方式 | PUT |
请求路径 | /category |
请求参数 | {id: "1399923597874081794", name: "超值午餐", sort: 0} |
5.3 代码实现
html页面中相关的代码都已经提供好了,我们已经分析了请求的信息,接下来就可以来创建服务端的CategoryController方法update方法。
<span style="background-color:#f8f8f8"><span style="color:#333333">/** * 根据id修改分类信息 * @param category * @return */ @PutMapping public R<String> update(@RequestBody Category category){ log.info("修改分类信息:{}",category); categoryService.updateById(category); return R.success("修改分类信息成功"); }</span></span>
5.4 功能测试
按照前面分析的操作流程进行测试,查看数据是否正常修改即可。