瑞吉外卖07-对于分类信息的CRUD
数据模型
分类信息我们要用一个新的表来存储——category
我们添加的菜品/套餐名称,是唯一的,不能够重复的,所以在设计表结构时,已经针对于name字段建立了唯一索引 。
dish(菜品表)
setmeal(套餐表)
需求分析
1. 新增分类
后台系统中可以管理分类信息,分类包括两种类型,分别是 菜品分类 和 套餐分类 。当我们在后台系统中添加菜品时需要选择一个菜品分类,当我们在后台系统中添加一个套餐时需要选择一个套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐。
在分类管理中,我们新增分类时,可以选择新增菜品分类(川菜、湘菜、粤菜...),也可以选择新增套餐分类(营养早餐、超值午餐...)。
在添加套餐的时候,输入的排序字段,控制的是移动端套餐列表的展示顺序。
2. 分类信息分页查询
这个点说白了就是实现一个分页查询功能。
3. 删除分类
在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时,此分类不允许删除。
-
根据当前分类的ID,查询该分类下是否存在菜品,如果存在,则提示错误信息
-
根据当前分类的ID,查询该分类下是否存在套餐,如果存在,则提示错误信息
4. 修改分类
在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作。
注:这里的回显是前端帮我们做的!
前端页面分析
1. 新增分类
在开发代码之前,需要梳理一下整个程序的执行过程
(1)在页面(backend/page/category/list.html)的新增分类表单中填写数据,点击 "确定" 发送ajax请求,将新增分类窗口输入的数据以json形式提交到服务端
(2)服务端Controller接收页面提交的数据并调用Service将数据进行保存
(3)Service调用Mapper操作数据库,保存数据
可以看到新增菜品分类和新增套餐分类请求的服务端地址和提交的json数据结构相同,所以服务端只需要提供一个方法统一处理
<el-button
v-if="action != 'edit'"
type="primary"
size="medium"
class="continue"
@click="submitForm('go')"
> 保存并继续添加 </el-button>
// 新增接口
const addCategory = (params) => {
return $axios({
url: '/category',
method: 'post',
data: { ...params }
})
}
所以说当我们在前端页面点击 “保存并继续添加” 时,就会发送POST的请求,具体如下:
请求 | 说明 |
---|---|
请求方式 | POST |
请求路径 | /category |
请求参数 | json格式 - {"name":"川菜","type":"1","sort":2} |
2. 分类信息分页查询
在开发代码之前,需要梳理一下整个程序的执行过程
(1)页面发送ajax请求,将分页查询参数(page、pageSize)提交到服务端
(2)服务端Controller接收页面提交的数据并调用Service查询数据
(3)Service调用Mapper操作数据库,查询分页数据
(4)Controller将查询到的分页数据响应给页面
(5)页面接收到分页数据并通过ElementUI的Table组件展示到页面上
// 查询列表接口
const getCategoryPage = (params) => {
return $axios({
url: '/category/page',
method: 'get',
params
})
}
具体的请求信息整理如下
请求 | 说明 |
---|---|
请求方式 | GET |
请求路径 | /category/page |
请求参数 | ?page=1&pageSize=10 |
3. 删除分类
在开发代码之前,需要梳理一下整个程序的执行过程
(1)点击删除,页面发送ajax请求,将参数(id)提交到服务端
(2)服务端Controller接收页面提交的数据并调用Service删除数据
(3)Service调用Mapper操作数据库
// 删除当前列的接口
const deleCategory = (id) => {
return $axios({
url: '/category',
method: 'delete',
params: { id }
})
}
请求 | 说明 |
---|---|
请求方式 | DELETE |
请求路径 | /category |
请求参数 | ?id=1395291114922618881 |
4. 修改分类
请求 | 说明 |
---|---|
请求方式 | PUT |
请求路径 | /category |
请求参数 | {id: "1399923597874081794", name: "超值午餐", sort: 0} |
代码实现
CategoryMapper
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}
CategoryService
public interface CategoryService extends IService<Category>{
R<String> saveSortInfo(Category category);
R<String> deleteSortInfo(Long id);
R<Page> pageSortInfo(int page, int pageSize);
R<String> updateSortInfo(Category category);
}
CategoryServiceImpl
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private SetmealMapper setmealMapper;
@Override
public R<String> saveSortInfo(Category category) {
categoryMapper.insert(category);
return R.success("新增分类成功!");
}
@Override
public R<String> deleteSortInfo(Long id) {
// 添加查询条件,根据分类id进行查询菜品数据
LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);
int count1 = dishMapper.selectCount(dishLambdaQueryWrapper);
// 如果已经关联,抛出一个业务异常
if (count1 > 0) {
throw new CustomException("当前分类下关联了菜品,不能删除!");
}
// 查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常
LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);
int count2 = setmealMapper.selectCount(setmealLambdaQueryWrapper);
if (count2 > 0) {
throw new CustomException("当前分类下套餐了菜品,不能删除!");
}
super.removeById(id);
return R.success("分类信息删除成功!");
}
@Override
public R<String> updateSortInfo(Category category) {
categoryMapper.updateById(category);
return R.success("修改完成!");
}
@Override
public R<Page> pageSortInfo(int page, int pageSize) {
Page<Category> pageInfo = new Page<>(page, pageSize);
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByAsc(Category::getSort);
categoryMapper.selectPage(pageInfo, queryWrapper);
return R.success(pageInfo);
}
}
CategoryController
/**
* 分类管理
*/
@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
* 新增菜品
* @param category
* @return
*/
@PostMapping
public R<String> saveSortInfo(@RequestBody Category category) {
return categoryService.saveSortInfo(category);
}
/**
* 删除菜品,根据id删除分类,删除之前做一下判断,是否有关联
* @param id
* @return
*/
@DeleteMapping
public R<String> deleteSortInfo(Long id) {
return categoryService.deleteSortInfo(id);
}
/**
* 修改菜品
* @param category
* @return
*/
@PutMapping
public R<String> updateSortInfo(@RequestBody Category category) {
return categoryService.updateSortInfo(category);
}
/**
* 分页
* @param page
* @param pageSize
* @return
*/
@GetMapping("/page")
public R<Page> pageSortInfo(int page, int pageSize) {
return categoryService.pageSortInfo(page, pageSize);
}
}
创建自定义异常
/**
* 自定义业务异常类
*/
public class CustomException extends RuntimeException{
public CustomException(String msg) {
super(msg);
}
}
GlobalExceptionHandler
/**
* 全局异常处理
* @ControllerAdvice: 表示拦截 类上有 @RestController 注解的类
* @ResponseBody:封装成JSON数据
*/
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if(ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg = split[2] + "已存在";
return R.error(msg);
}
return R.error("未知错误");
}
/**
* 异常处理方法
* @return
*/
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}