分类管理主要是通过添加菜品分类和套餐分类来实现前端的分类展示
话不多说直接上图
可以看出,分类管理的数据显示在了前端的菜品栏中,那么接下来我们就来实现这个功能
需求 |
---|
在所有表中,都存在create_user 、update_user、create_time、update_time |
这些公共字段修改和添加时每次都需要我们去处理,现在改为统一处理,简化开发 |
步骤 | 过程 |
---|---|
1 | 在实体类属性上添加@TableField注释,指定自动填充策略 |
2 | 编写原数据对象处理器,统一为公共字段赋值 |
首先对EmployeeServiceImpl
类下的addEmployee
和updateStatus
方法做出修改
看一下自己的Employee
类中是否已经加入@TableField注解
在注入createUser和updateUser时,是需要获取存在网页的session的。但在MyMetaObjectHandler类的方法中是无法获取request对象的。这里需要引入一个线程的思想。
什么是ThreadLocal?
ThreadLocal是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
在common包下创建BaseContext
类
BaseContext.java
实现对ThreadLocal方法的封装
package com.cjgn.common;
/**
* 基于ThreadLocal封装工具类,用户保存和获取当前登录的id
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
public static void setCurrentId(Long id) {
threadLocal.set(id);
}
public static Long getCurrentId(){
return threadLocal.get();
}
}
LoginInterceptor.java
在登录模块我们写过拦截器,每个请求都会经过拦截器,那么我们就在拦截器中把这次请求的id保存在线程中。于是把拦截器类中的
preHandle
方法做出修改
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
System.out.println("本次拦截路径" + requestURI);
//获取session中的id
Long id = (Long) request.getSession().getAttribute("employee");
//4、判断登录状态,如果已经登录则放行
if ( id != null) {
//将id存入到线程中
BaseContext.setCurrentId(id);
System.out.println("已登录");
return true;
}
//5、如果没有登录则跳转到登录页面
else {
//使用response传回json数据
response.getWriter().write(JSON.toJSONString(new Result(Code.ERR, "NOTLOGIN")));
System.out.println("未登录");
return false;
}
}
在common包下创建MyMetaObjectHandler
类
MyMetaObjectHandler.java
这个类的作用就很简单了,还记得上面添加的注释吗,在这个类中将对注释的公共字段进行填充
这样在执行更新和添加操作时,就会自动填充这些属性的值
package com.cjgn.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
* 自定义元数据对象处理器
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时自动填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("公共字段自动填充[insert]...");
//填充时间
metaObject.setValue("createTime",LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
//获取存在线程中的id
Long id = BaseContext.getCurrentId();
//存入id
metaObject.setValue("createUser",id);
metaObject.setValue("updateUser",id);
}
/**
* 更新时自动填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("公共字段自动填充[update]...");
//存入更新时间
metaObject.setValue("updateTime",LocalDateTime.now());
//获取存在线程中的id
Long id = BaseContext.getCurrentId();
//存入id
metaObject.setValue("updateUser",id);
}
}
需求 |
---|
可以在后台系统中添加菜品分类和套餐分类 |
之后在添加菜品时,需要选择我们添加的菜品分类 |
之后在添加套餐时,需要选择我们添加的套餐分类 |
可以为套餐添加排序sort,排序为展示的顺序 |
添加完成后,在页面实现分页展示效果,并按照sort进行排序 |
步骤 | 过程 |
---|---|
1 | 将前端传回的数据拦截后发送给service层 |
2 | 在service层调用mapper插入数据 |
3 | 在service层编写分页查询代码,和员工分页查询基本一致 |
表现层:CategoryController.java
package com.cjgn.contorller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cjgn.common.Code;
import com.cjgn.common.Result;
import com.cjgn.entity.Category;
import com.cjgn.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/category")
@Slf4j
public class CategoryController {
@Autowired
CategoryService categoryService;
/**
* 添加分类的信息
* @param category 前端过来的对象
* @return
*/
@PostMapping
public Result addCategory(@RequestBody Category category){
//调用service添加数据
boolean flag = categoryService.addCategory(category);
Integer code = flag? Code.OK:Code.ERR;
String msg = flag?"添加成功":"添加失败";
return new Result(code,msg);
}
/**
* 对分类信息进行分页查询
* @param page 页数
* @param pageSize 页面大小
* @return
*/
@GetMapping("/page")
public Result selectByPage(int page,int pageSize){
log.info("page="+page+";pageSize="+pageSize);
//调用service查询数据
Page page1 = categoryService.selectByPage(page, pageSize);
//查询结果是可能为空的,所以如果不发生异常,统一返回成功
Integer code = Code.OK;
String msg = "查询成功";
return new Result(page1,code,msg);
}
}
业务层:CategoryService.java
package com.cjgn.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cjgn.entity.Category;
public interface CategoryService {
/**
* 新增分类
* @param category 传过来的对象值
* @return
*/
public boolean addCategory(Category category);
/**
* 分类的分页查询
* @param page 第几页
* @param pageSize 每页多少条
* @return
*/
public Page selectByPage(int page,int pageSize);
}
CategoryServiceImpl.java
package com.cjgn.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cjgn.entity.Category;
import com.cjgn.mapper.CategoryMapper;
import com.cjgn.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
/**
* 新增分类
* @param category 传过来的对象值
* @return
*/
@Override
public boolean addCategory(Category category) {
//添加数据并返回一个boolean值
int i = categoryMapper.insert(category);
return i>0;
}
/**
* 分类信息的分页查询
* @param page 第几页
* @param pageSize 每页多少条
* @return
*/
@Override
public Page selectByPage(int page, int pageSize) {
//设置page对象
Page<Category> iPage = new Page<>(page,pageSize);
//设置条件,按照type的升序排序
LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<Category>();
wrapper.orderByAsc(Category::getSort);
//查询数据,并返回给表现层
categoryMapper.selectPage(iPage,wrapper);
return iPage;
}
}
数据层:CategoryMapper.java
package com.cjgn.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cjgn.entity.Category;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}
实体类:Category.java
package com.cjgn.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.time.LocalDateTime;
//菜品套餐分类实体类
@Data
public class Category {
private Long id;
//1为菜品分类,2为套餐分类
private Integer type;
//分类名称,唯一
private String name;
//排序
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;
}
需求 |
---|
点击删除按钮时,删除这个分类数据,只有分类下没数据时才能删除成功 |
如果此分类关联了菜品或套餐数据,则提示不能删除 |
可以通过修改按钮来修改分类的名字和排序 |
步骤 | 过程 |
---|---|
1 | 接收前端传来的id信息,发送到service进行处理 |
2 | 在service中判断,dish和setmeal中是否有关联此id的数据 |
3 | 自定义一个异常,并用全局异常去处理这个异常 |
4 | 如果关联了id数据,则抛出一个自定义异常;如果没有,则删除数据 |
5 | 接收前端传回的json数据,并调用service修改对应的数据信息 |
实体类:Dish.java
package com.cjgn.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
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;
}
Setmeal.java
package com.cjgn.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
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;
}
数据层:DishMapper.java
package com.cjgn.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cjgn.entity.Dish;
import org.apache.ibatis.annotations.Mapper;
/**
* 菜品
*/
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}
SetmealMapper.java
package com.cjgn.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cjgn.entity.Setmeal;
import org.apache.ibatis.annotations.Mapper;
/**
* 套餐
*/
@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal> {
}
业务层:CategoryService.java(新增)
/**
* 根据id删除分类
* @param id
* @return
*/
public boolean remove(Long id);
/**
* 修改分类的信息
* @param category
* @return
*/
public boolean updateCategory(Category category);
CategoryServiceImpl.java(整体修改)
package com.cjgn.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cjgn.common.Code;
import com.cjgn.entity.Category;
import com.cjgn.entity.Dish;
import com.cjgn.entity.Setmeal;
import com.cjgn.execption.CustomExecption;
import com.cjgn.mapper.CategoryMapper;
import com.cjgn.mapper.DishMapper;
import com.cjgn.mapper.SetmealMapper;
import com.cjgn.service.CategoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private SetmealMapper setmealMapper;
/**
* 新增分类
*
* @param category 传过来的对象值
* @return
*/
@Override
public boolean addCategory(Category category) {
//添加数据并返回一个boolean值
int i = categoryMapper.insert(category);
return i > 0;
}
/**
* 分类信息的分页查询
*
* @param page 第几页
* @param pageSize 每页多少条
* @return
*/
@Override
public Page selectByPage(int page, int pageSize) {
//设置page对象
Page<Category> iPage = new Page<>(page, pageSize);
//设置条件,按照type的升序排序
LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<Category>();
wrapper.orderByAsc(Category::getSort);
//查询数据,并返回给表现层
categoryMapper.selectPage(iPage, wrapper);
return iPage;
}
/**
* 根据id删除分类数据
*
* @param id
* @return
*/
@Override
public boolean remove(Long id) {
//查询当前分类是否关联了菜品,如果有则抛出一个业务异常
LambdaQueryWrapper<Dish> wrapper1 = new LambdaQueryWrapper<Dish>();
//在dish表中查询数据
wrapper1.eq(Dish::getCategoryId, id);
//查询总数大于0则为关联
Integer count1 = dishMapper.selectCount(wrapper1);
if (count1 > 0) {
throw new CustomExecption(Code.ERR, "当前分类下关联了菜品,不能删除");
}
//查询当前分类是否关联了套餐,如果有则抛出一个业务异常
LambdaQueryWrapper<Setmeal> wrapper2 = new LambdaQueryWrapper<Setmeal>();
//在setmeal表中查询数据
wrapper2.eq(Setmeal::getCategoryId, id);
//查询总数大于0则为关联
Integer count2 = setmealMapper.selectCount(wrapper2);
if (count2 > 0) {
throw new CustomExecption(Code.ERR, "当前分类下关联了套餐,不能删除");
}
//正常删除分类
int i = categoryMapper.deleteById(id);
return i > 0;
}
/**
* 修改分类的信息
*
* @param category
* @return
*/
@Override
public boolean updateCategory(Category category) {
int i = categoryMapper.updateById(category);
return i > 0;
}
}
表现层:CategoryController.java(新增)
/**
* 删除分类
* @param id
* @return
*/
@DeleteMapping
public Result remove(@RequestParam("ids") Long id){
boolean flag = categoryService.remove(id);
Integer code = flag? Code.OK:Code.ERR;
String msg = flag?"删除成功":"删除失败";
return new Result(code,msg);
}
/**
* 修改分类的信息
* @param category
* @return
*/
@PutMapping
public Result updateCategory(@RequestBody Category category){
boolean flag = categoryService.updateCategory(category);
Integer code = flag? Code.OK:Code.ERR;
String msg = flag?"修改成功":"修改失败";
return new Result(code,msg);
}
自定义异常:CustomExecption.java
package com.cjgn.execption;
import lombok.Data;
@Data
public class CustomExecption extends RuntimeException{
//代表返回的编码信息
private Integer code;
public CustomExecption(Integer code,String message) {
super(message);
this.code = code;
}
}
全局异常处理:GlobalExecptionHandler.java(新增)
/**
* 违反参照完整性异常处理方法
* @param ex
* @return
*/
@ExceptionHandler(CustomExecption.class)
public Result doExecption(CustomExecption ex){
//输出异常信息
log.error(ex.getMessage());
//返回一个Result全局对象
return new Result(ex.getCode(),ex.getMessage());
}
到此处,分类部分的开发基本完成。在分类开发中,主要的新重点如下
因为篇幅问题,每个部分都完整粘贴代码不太现实,所以就采用了只粘贴新增的代码的形式,需要自己去寻找之前写过的类,去添加对应的代码。希望大家理解