瑞吉外卖-分类管理

目录

  • |总体效果
  • |公共字段自动填充
    • +需求分析
    • +代码实现
      • 前置处理
      • ThreadLocal
  • |新增分类与分类分页查询
    • +需求分析
    • +代码实现
  • |修改和删除分类
    • +需求分析
    • +代码实现
  • |总结

|总体效果

分类管理主要是通过添加菜品分类套餐分类来实现前端的分类展示

话不多说直接上图

瑞吉外卖-分类管理_第1张图片
瑞吉外卖-分类管理_第2张图片

可以看出,分类管理的数据显示在了前端的菜品栏中,那么接下来我们就来实现这个功能

|公共字段自动填充

+需求分析

需求
在所有表中,都存在create_user 、update_user、create_time、update_time
这些公共字段修改和添加时每次都需要我们去处理,现在改为统一处理,简化开发

+代码实现

步骤 过程
1 在实体类属性上添加@TableField注释,指定自动填充策略
2 编写原数据对象处理器,统一为公共字段赋值

前置处理

首先对EmployeeServiceImpl类下的addEmployeeupdateStatus方法做出修改
瑞吉外卖-分类管理_第3张图片
瑞吉外卖-分类管理_第4张图片
看一下自己的Employee类中是否已经加入@TableField注解

瑞吉外卖-分类管理_第5张图片

ThreadLocal

在注入createUser和updateUser时,是需要获取存在网页的session的。但在MyMetaObjectHandler类的方法中是无法获取request对象的。这里需要引入一个线程的思想。

  • 客户端每次发送的Http请求,对应的在服务端都会分配一个新的线程来处理,那么就可以使用当前线程来获取网页session中的数据

什么是ThreadLocal?

ThreadLocal是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

  • 那么问题就解决了,每次请求都会经过拦截器,只需要在拦截器中将从session中获取的id存到线程中,在填充数据时从线程中获取出来就好了。

在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());
    }

|总结

到此处,分类部分的开发基本完成。在分类开发中,主要的新重点如下

  • 字段自动填充:实现在添加和更新时自动填充对应字段,适用于所有表都拥有的公共字段
  • 线程:对于公共字段类无法获取request对象,需要将id信息存入线程中,然后在类中获取。此方法基于前端的每次请求都会在服务端创建一个新的线程去执行。
  • 参照完整性:在删除数据库数据时,如果数据主键有关联的外键,则不能直接删除,需要先删除外键的内容,再删除主键。

因为篇幅问题,每个部分都完整粘贴代码不太现实,所以就采用了只粘贴新增的代码的形式,需要自己去寻找之前写过的类,去添加对应的代码。希望大家理解

你可能感兴趣的:(瑞吉外卖,spring,boot,maven,spring,mybatis)