瑞吉外卖项目Day2———完善登录问题员工功能

Posted 小小程序○

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了瑞吉外卖项目Day2———完善登录问题员工功能相关的知识,希望对你有一定的参考价值。

创建过滤器类(filter)

package com.study.filter;

import com.alibaba.fastjson.JSON;
import com.study.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//注解配置拦截器名称(类名),拦截路径"/*"
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter 
    //1.路径匹配器,支持通配符
    public static  final AntPathMatcher PATH_MATCHER=new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException 
        HttpServletRequest request=(HttpServletRequest) servletRequest;
        HttpServletResponse response=(HttpServletResponse) servletResponse;

        //1.获取本次请求的URI
        String requestURI =request.getRequestURI();
        //2.不需要处理的路径定义
        String[] urls=new String[]
                  "/employee/login",
                  "/employee/logout",
                  "backend/**",
                  "front/**"
        ;

        //3.判断此次请求是否需要处理
        boolean flag=check(urls,requestURI);

        //4.如果不需要处理,直接放行
        if(flag)
            log.info("本次请求不需要处理",requestURI);
            filterChain.doFilter(request,response);
            return;
        

        //4.判断登录状态,如果已经登录,则直接放行
        //查看浏览器session里面是否存在id
        if(request.getSession().getAttribute("employee")!=null)
            log.info("用户已经登录,用户id为:",request.getSession().getAttribute("employee"));
            filterChain.doFilter(request,response);
            return;
        

        log.info("用户未登录");
        //5.如果未登录则返回未登录结果,通过输出流方式向客户端响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    


    /**
     * 检查此次请求是否需要放行
     * @param urls
     * @param requestURI
     * @return
     */
    public  boolean check(String[] urls,String requestURI)
        for(String url:urls)
            boolean match=PATH_MATCHER.match(url,requestURI);
            if(match)
                return true;
            
        
        return false;
    


新增员工(Controller)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yirUoxB9-1679626714687)

在Controller包中的EmployeeController添加如下方法:

/**
     * 新增员工
     * @param request
     * @param employee
     * @return
     */
    @PostMapping
    public R<String> save(HttpServletRequest request,@RequestBody Employee employee)
        log.info("新增员工,员工信息",employee.toString());

        //设置初始密码为123456并且利用m5加密处理
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        //获取当前登录用户的id
        Long empId =(Long) request.getSession().getAttribute("employee");

        //设置id和修改id
        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);

        employeeService.save(employee);
        
        return R.success("新增员工成功");
    

分页查询

1.创建MybatisPlusConfig类配置分页插件

package com.study.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 配置MP的分页插件
 */
@Configuration
public class MybatisPlusConfig 

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor()
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    

2.在EmployeeController增加分页查询方法

   @GetMapping("/page")
    public R<Page> page(int page,int pageSize,String name)
        log.info("page=,pageSize = name=",page,pageSize,name);
        //构造分页构造器
        Page pageInfo=new Page(page,pageSize);
        //构造条件构造器
        LambdaQueryWrapper<Employee> queryWrapper=new LambdaQueryWrapper<>();
        //添加过滤条件
        queryWrapper.like(!StringUtils.isEmpty(name),Employee::getName,name);
        //添加排序条件
        queryWrapper.orderByDesc(Employee::getUpdateTime);
        //执行查询
        employeeService.page(pageInfo,queryWrapper);
        return R.success(pageInfo);
    

启用禁止员工

1.前端代码展示

2.增加修改和查询数据方法(controller)

/**
     * 根据id修改员工信息
     * @param request
     * @param employee
     * @return
     */
    @PutMapping
    public R<String> update(HttpServletRequest request,@RequestBody Employee employee)
        log.info(employee.toString());
        long empId=(long)request.getSession().getAttribute("employee");
        //设置更新时间
        employee.setUpdateTime(LocalDateTime.now());
        //更新用户id
        employee.setUpdateUser(empId);
        //根据id进行更新
        employeeService.updateById(employee);

        return R.success("员工信息修改成功");
    

功能无法实现[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5npd8GPw-1679626714688)

原因:客户端将id(long)传递给服务器,服务器无法保留id的精确度(只能保留到前16类,后面三位采取四舍五入的方式保留226–>200)

1.解决:创建JackSonObjectMapper类(common)

package com.study.common;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper 

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() 
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                  .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                  .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                  .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                  .addSerializer(BigInteger.class, ToStringSerializer.instance)
                  .addSerializer(Long.class, ToStringSerializer.instance)
                  .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                  .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                  .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    


2.在WebMvcConfig中添加如下方法扩张MVC框架消息转换器

/**
     * 扩张MVC框架消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) 
        //创建消息转换器
        MappingJackson2HttpMessageConverter messageConverter=new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用JackSon将java转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器追加到MVC框架的转换器集合中,index越小越优先使用
        converters.add(0,messageConverter);
    

编辑员工

add.html获取客户端编辑客户的id,并将id传到服务端。

服务端接受请求查询员工信息,并以json格式响应到页面。

服务端接收json数据,并将员工信息回显。再次点击保存按钮,

将员工信息传到服务器中,并且调用save方法进行保存

在EmployeeController类中添加getById方法查询数据

  @GetMapping("/id")
    public R<Employee> getById(@PathVariable long id)
        log.info("根据id查询员工信息...");
        Employee employee=employeeService.getById(id);
        if(employee!=null)
            return R.success(employee);
        
        return R.error("没有查询到对应员工信息");
    

异常处理

1.创建异常类进行全局异常捕获

2.创建GlobalExceptionHandler 类

package com.study.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLIntegrityConstraintViolationException;


@ControllerAdvice(annotations = RestController.class, Controller.class)
@ResponseBody
@Slf4j
public class GlobalExceptionHandler 
    /**
     * 异常处理方法
     * @param ex
     * @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] + "已存在";
            R.error(msg);
        

        return R.error("未知错误");
    

SpringBoot项目SpringBoot项目-瑞吉外卖day03分类管理

文章目录

🌕博客x主页:己不由心王道长🌕!
🌎文章说明:SpringBoot项目-瑞吉外卖【day03】分类管理🌎
✅系列专栏:SpringBoot项目
🌴本篇内容:对黑马的瑞吉外卖项目的day03进行笔记和项目实现🌴
☕️每日一语:生活不可能像你想象得那么好,但也不会像你想象得那么糟。☕️
🚩 交流社区:己不由心王道长(优质编程社区)

前言

本次文章对应所属项目的第3天,我在想,我项目进度到底是快了还是慢了。这个问题有点深奥,如果对于官方给的进度,那我项目肯定是慢了;但是项目得消化,不能做完即可,图个完成任务的心态是不可取的,所以还是慢慢来吧。

公共字段自动填充

问题分析

我们在day02已经对后台的员工管理功能进行了开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段信息,在编辑员工时需要设置修改时间和修改人等字段信息。这些字段都是属于公共字段,也就是很多表中都有的字段,如下所示:


基本每个表都有以上字段,而且我们在每一个需要用到的修改、新增时都用到了这些公共字段。



这些代码十分冗余,没有技术含量,每次都写一遍是不可接受的。那么我们能不能对于这些公共字段做一个统一的处理,以便简化开发,让代码更加美观呢?可以!
MybatisPlus为我们提供了公共字段自动填充功能。
Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。
实现步骤:

1、在实体类的属性上加入@TableField注解,指定自动填充的策略


可以看到,我们能在相应的公共字段上,添加@TableField注解,然后在括号里选择方式,最后选择填充策略。填充策略有默认、插入、插入和更新、更新四种。

@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;

以上是公共字段填充,为什么填充策略不同呢?这里解释一下:

因为createTime只有在新建的时候使用,而updateTime在插入的时候就已经算更新了,在后面的更新中当然也算。所以updateTime的策略是插入和更新时填充,同理可以理解其他几个。

2.按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口
新建一个MyMetaObjectHandler:

代码实现

package com.example.commons;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

/**
 * 自定义公共字段自动填充
 * @author 不止于梦想
 * @date 2022/11/15 20:23
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler 
    /**
     * insert策略填充
     * @param metaObject
     */
    @Override
    public  void insertFill(MetaObject metaObject) 
        log.info(metaObject.toString());
        log.info("insert填充策略......");
    

    /**
     * update策略填充
     * @param metaObject
     */

    @Override
    public void updateFill(MetaObject metaObject) 
        log.info(metaObject.toString());
        log.info("update填充策略......");
    


这里没有进行任何填充,先测试一下代码是否能够走通

功能测试

我们在update策略里输出日志并且打上断点,验证我们程序是否能够执行成功。

这是更新策略,所以我们修改员工信息:

可以看到,代码是可以走通的。并且是在UPDATE之前,这就是我们想看到的

功能完善

这里其实把上面没写的代码一并在这里完成,这里原本是解决ThreadLocal问题的,一并解决了吧。
先把update的里面这几句注释掉,现在要用公共字段填充,这些不写了,拜拜勒:

重启项目发送更新请求:

注意看参数,update时间跟我当前时间不符合,说明了现在没有填充时间。下面依次完成需要的填充:

package com.example.commons;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 自定义公共字段自动填充
 * @author 不止于梦想
 * @date 2022/11/15 20:23
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler 
    /**
     * insert策略填充
     * @param metaObject
     */
    @Override
    public  void insertFill(MetaObject metaObject) 
        metaObject.setValue("createTime",LocalDateTime.now());
        log.info("insert填充策略......");
    

    /**
     * update策略填充
     * @param metaObject
     */

    @Override
    public void updateFill(MetaObject metaObject) 
        metaObject.setValue("updateTime", LocalDateTime.now());
        log.info("update填充策略......");
    


上面这段代码是不完整的,没有设置本次插入或者更新的人的id,我们能不能用session对象设置呢?不行,因为在方法执行的时候,真正的方法压根没有明着调用我们这个公共填充,而一次request请求你也给不了它。

解决办法,首先我们要知道的是一次request请求其实对应的是一次线程,而我们要用到的线程是JDK为我们提供的ThreadLocal.
这里我们先要确认一件事情,就是每当前台发一次http请求,我们后台对应的服务器是不是分配了一个新的线程来处理:

多余解释画蛇添足,下面是官方给的方法,我们可以试试:

在处理过程中涉及到下面类中的方法都属于相同的一个线程:
1、LoginCheckFilter的doFilter方法
2、EmployeeController的update方法
3、MyMetaObjectHandler的updateFill方法
可以在上面的三个方法中分别加入下面代码 (获取当前线程id):
long id Thread. current Thread() getId() :
Log. info(“线程id:1”,id) :
执行编辑员工功能进行验证,通过观察控制台输出可以发现,一次请求对应的线程id是相同的:



可以知道的是一次请求确实是对应一个线程,还得验证一件事情,就是不同请求不是一次线程。再发一次:

既然每次请求对应一个线程,我们不可以共有一个请求,一个线程我们是可以共享的,而且别的请求线程也影响不到你的线程。

介绍ThreadLocal:

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

public void set(T value):设置当前线程的线程局部变量的值
public T get() :返回当前线程所对应的线程局部变量的值

我们可以在LoginCheckFilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMetaobjectHandler的updateFil方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。

有了步骤咱就整它,打它啊,打它mad!:

实现步骤:
1、编写BaseContext工具类,基于ThreadLocal封装的工具类

package com.example.commons;

/**
 * @author 不止于梦想
 * @date 2022/11/15 21:45
 */
public class BaseContext 
    //设置成静态属性
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    /**
     * 设置线程局部变量
     * @param id
     */
    public static void setCurrentId(Long id)
        threadLocal.set(id);
    

    /**
     * 获取线程局部变量的值
     * @return
     */
    public static Long getCurrentId()
        return threadLocal.get();
    


2、在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id

if (httpServletRequest.getSession().getAttribute("employee")!=null) 
            log.info("用户已经登录"+httpServletRequest.getSession().getAttribute("employee"));
            //获取当前请求的用户id
            long empId = (long) httpServletRequest.getSession().getAttribute("employee");
           //设置当前线程的线程局部变量的值
            BaseContext.setCurrentId(empId);
            filterChain.doFilter(httpServletRequest,httpServletResponse);

只有已经登录过的用户才能获取到对应的id。
3、在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

 metaObject.setValue("updateUser",BaseContext.getCurrentId());

测试:

最后把所有公共字段去掉

新增分类

需求分析


在我们的分类管理中,有两个新增分类,分别是新增菜品分类和新增套餐分类。


新增菜品分类和新增套餐分类其实基本无差别,只是发给后台时的type属性不同。
在这个功能中,我们需要连接前端,并且在后端区分,然后把操作数据存入数据库。


调用了axios、方法是post方法。

只判断了code,所以新的controller应该是String类型。

模型

在这个功能中,我们的数据模型跟前面的不一样了,不再是employee,而是category

两个分类模式,数据其实存入了一张表之中。

需要注意的是这里的name设置了唯一性约束,如果名字重复是会抛出异常的

代码开发

这里直接导入类category:

package com.example.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 lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 分类
 */
@Data
public class Category implements Serializable 

    private static final long serialVersionUID = 1L;

    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;



接下来把service、mapper弄好。
然后·写好controller:

package com.example.controller;

import com.example.commons.R;
import com.example.entity.Category;
import com.example.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 不止于梦想
 * @date 2022/11/17 18:21
 */
@RestController
@RequestMapping("/category")
@Slf4j
public class CategoryController 

    @Autowired
    CategoryService categoryService;

    /**
     * 新增分类
     * @param category
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody Category category)
        log.info("新增分类");
        boolean save = categoryService.save(category);
        if(save) 
            return R.success("新增分类成功");
        
        return R.error("新增分类失败");
    





功能测试



添加分类成功,接口异常是接下来要处理的信息分页查询。

这里还有一个问题就是,如果我们添加的菜品名字一样,会出异常的,因为我们设计表时就已经把name字段设置为非空了。我们测试一下

但是我们不会受到影响,因为我们在前面已经设置了一个全局处理异常“Duplicate entry”

所以会提示我们已经存在。

分类信息分页查询

需求分析

在上面的新增分类,我们已经提到了系统接口404异常,那么这个异常其实就是当我们点击分类管理时,页面就会发送请求去后台查询数据并且返回展示了:

由上图,当我们点击新增分类时,vue就创建了钩子函数,并调用了getCategoryPage方法。并且传入了页码和页码所在页的大小。其实就是一个分页查询,我们在employee时已经做过,所以这里直接跟进getCategoryPage:

细节如图。

代码开发

@GetMapping("/page")
    public R<Page> page(int page,int pageSize)
        log.info("分页查询");
        //构造分页构造器
        Page pageInfo = new Page(page,pageSize);
        //构造条件构造器,输出时要用到
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(Category::getSort);
        //进行分页查询
        categoryService.page(pageInfo, queryWrapper);
        return R.success(pageInfo);
    

功能测试

删除分类

需求分析


可以看到,我们的分类管理后面其实是可以操作的,而这里要介绍的操作就是删除分类。
这里需要注意是当分类关联了菜品或者套餐时,此分类是不允许删除的。这里解释一下,我们这里只是套餐分类,真正的细节并不是存在这个表里的,而是分别存在相应的表中:


如上图,分类表只能表示有没有当前种类和添加种类,删除不归它管理,如果不存在该种类,查询时自然不显示。

代码开发

我们还是先做简单的删除


注意这里通过id删除,但参数传递时是ids

  @DeleteMapping
    public R<String> delete(Long ids)
        log.info("删除操作......");
        categoryService.removeById(ids);
        return  R.success("删除成功");
    

功能完善

这里细节就不多说了。
上代码,不过那些需要导入和构建架构的代码就不上了,太水:
CustomExcption:

package com.example.commons;

/**
 * @author 不止于梦想
 * @date 2022/11/17 21:00
 */

/**
 * 自定义异常
 */
public class CustomerExcption extends RuntimeException
    /**
     * 传入异常信息,交给父类
     * @param msg :异常信息
     */
    public CustomerExcption(String msg)
        super(msg);
    


CategoryServiceImpl:

package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.commons.CustomerExcption;
import com.example.entity.Category;
import com.example.entity.Dish;
import com.example.entity.Setmeal;
import com.example.mapper.CategoryMapper;
import com.example.service.CategoryService;
import com.example.service.DishService;
import com.example.service.SetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author 不止于梦想
 * @date 2022/11/17 18:19
 */
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService 
    @Autowired
    DishService dishService;
    @Autowired
    SetmealService setmealService;

    /**
     * 通过id删除分类,删除之前检查有没有关联套餐(Setmeal)或者菜品(Dish),需要用到这两者的服务,所以在上边进行注入
     * @param id
     */
    @Override
    public void remove(Long id) 
        //判断是否关联Dish,设置查询条件
        LambdaQueryWrapper<Dish> dish = new LambdaQueryWrapper<>();
        //菜品分类id
        //    private Long categoryId;比较两者id是否相等
        //设置条件判断,条件为传入id与Dish表中的属性CategoryId相等
        dish.eq(Dish::getCategoryId,id);
        //调用dishService服务,查询相等的条数
        int count1 = dishService.count(dish);
        //如果存在,则说明关联,抛出异常,提示前台
        if(count1>0)
            throw new CustomerExcption("菜品已被关联,不能删除");
        
        //判断是否关联Dish,设置查询条件
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //分类id
        //private Long categoryId;
        //设置查询条件
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id)SpringBoot项目SpringBoot项目-瑞吉外卖day03分类管理

Java项目瑞吉外卖保姆级学习笔记(改项目名称+改邮件验证码登录+功能补充)

SpringBoot实现静态资源映射,登录功能以及访问拦截验证——以黑马瑞吉外卖为例

瑞吉外卖Java实战项目:从开发环境配置到后台功能

登录功能和退出功能(瑞吉外卖)

瑞吉外卖知识点总结