瑞吉外卖问题(待更新...


一、session注册时

刚开始使用的是该代码

   httpServletRequest.setAttribute("employee",emp.getId());

导致我点击登录后,还是一直在登录页面,进不去主页面。
原因是:
我使用的那行代码并没有保存到session里,只是保存到当前请求中。当请求别的页面时,就会失效。
正确代码:

httpServletRequest.getSession().setAttribute("employee", emp.getId());

该代码是保存到当前会话域。

移除的代码:

  httpServletRequest.getSession().removeAttribute("employee");

获取代码:

httpServletRequest.getSession().getAttribute("employee")

当时还比较迷惑一个地方,就是 已经已经排除掉以下页面,就是以下页面直接放行。但是当我请求/backend/index.html 时并不会被直接放行,还是需要先登录,经过检查,

 String[] urls=new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };

原因是:
index.html页面 会直接默认请求一个页面,
在这里插入图片描述
在这里插入图片描述
请求该页面就会被过滤器拦截。

二、用户增加时

ControllerAdvice注解给忘记了,通过该注解可以配置一个全局的信息,其中有一个属性是annotations,: 允许你指定一个或多个类(类型数组),只有继承或实现了这些类的控制器才会受到此 advice 影响。

用途:专门用于处理控制器层的全局异常处理、数据绑定和数据预处理。
应用范围:仅限于 Spring MVC 的控制器层。

原始代码: 使用的是ControllerAdvice

package com.cky.exceptions;

import com.cky.common.R;
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;

/**
 * @ClassName MyExceptions
 * @Description TODO
 * @Author lukcy
 * @Date 2024/6/21 15:51
 * @Version 1.0
 */
@Slf4j
@ResponseBody
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class MyExceptions {
    /**
     * 异常处理方法
     * @param ex
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> EmpExceptions(SQLIntegrityConstraintViolationException ex) {
        if (ex.getMessage().contains("")) {
            String[] split = ex.getMessage().split("");
            String msg = split[2] + "已存在";
            return R.error(msg);
        }return R.error("未知错误");
        }}

修改后
我没有使用该全局异常,而是在保存时先判断是否存在

public interface EmployeeService extends IService<Employee> {
  boolean isUsernameExist(String username);
}

@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 检查数据库中是否存在指定的用户名
     *
     * @param username 要检查的用户名
     * @return 如果存在返回true,否则返回false
     */
    public boolean isUsernameExist(String username) {
        QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        int count = employeeMapper.selectCount(queryWrapper);
        return count > 0;
    }
}
   @PostMapping
    public R<String> saveemp(HttpServletRequest httpServletRequest,@RequestBody Employee employee){
        boolean usernameExist = employeeService.isUsernameExist(employee.getUsername());
        if (usernameExist){
            return R.error(String.format("%s 用户名已经存在", employee.getUsername()));
        }
        //设置默认初始密码
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        //其他字段
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        Long id = (Long) httpServletRequest.getSession().getAttribute("employee");
        employee.setCreateUser(id);
        employee.setUpdateUser(id);
        //直接调用接口的save方法
        employeeService.save(employee);
        return R.success("员工添加成功");
    }

能够实现同样的功能
在这里插入图片描述

更正
到后边,比如新增菜品或者套餐时,发现还是需要判断名字是否唯一,所以还是需要全局异常类更好些。

package com.cky.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;

/**
 * @ClassName GlobalExceptiobHandler
 * @Description TODO
 * @Author lukcy
 * @Date 2024/6/25 9:56
 * @Version 1.0
 */
@Slf4j
@ResponseBody
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptiobHandler {

    @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("未知错误");
    }
}

三、RequestBody

在修改用户状态时:

3.1 Long问题

js 在对Long数据传送时,只会保证前16位有效,做法:增加一个自己的消息转换器+对象转换器,把Long数据转换为String。

package com.cky.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);
    }
}

package com.cky.config;

import com.cky.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

/**
 * @ClassName WebmvcConfig
 * @Description TODO
 * @Author lukcy
 * @Date 2024/5/29 9:40
 * @Version 1.0
 */
//默认能访问类路径下的static和template文件夹 这里我们没有放在这些文件夹下 所以访问不到,需要自己配置静态资源处理器
@Configuration
@Slf4j
public class WebmvcConfig extends WebMvcConfigurationSupport {
    /**
     * 静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }

    /**
     * 增加消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("配置的消息转换器....");
        MappingJackson2HttpMessageConverter mappingJackson2CborHttpMessageConverter=new MappingJackson2HttpMessageConverter();
        mappingJackson2CborHttpMessageConverter.setObjectMapper(new JacksonObjectMapper());
        converters.add(0,mappingJackson2CborHttpMessageConverter);
    }
}

3.2 RequestBody

刚开始在控制器上没有加上@RequestBody注解,导致传入的参数并不能赋给Employee类。
RequestBody和RequestParam区别
@RequestBody会将请求体中的数据,转换成对象

@RequestParam会从http请求查询参数中提取数据

@RequestParam和@RequestBody是Spring Framework中用于处理HTTP请求的注解,它们有以下区别:

1.数据来源:

@RequestParam: 从HTTP请求的查询参数中提取数据,即从URL中的?key=value形式的参数中获取数据。
@RequestBody: 从HTTP请求的请求体(body)中提取数据,通常用于接收JSON、XML等格式的数据。
2.用法:

@RequestParam: 通常用于处理GET请求或POST请求中的表单数据,例如?name=John&age=30这样的查询参数。
@RequestBody: 通常用于处理POST请求中的非表单数据,例如JSON格式的数据,或者XML格式的数据。
3.数据格式:

@RequestParam: 提取的数据一般是简单类型,如字符串、整数等。
@RequestBody: 提取的数据可以是复杂类型,如自定义的Java对象、Map、List等,通常是用于反序列化JSON或XML数据为Java对象。

3.3 UpdataById

由于Mybatis plus默认的更新策略是NOT_NULL:非 NULL;即通过接口更新数据时数据为NULL值时将不更新进数据库。所以Mybatis plus通过updateById(XXX)更新数据,当用户有更新字段为 空字符串 或者 null 的需求时,需要对 FieldStrategy 策略进行调整。

FieldStrategy 有三种策略:

IGNORED:0 忽略
NOT_NULL:1 非 NULL,默认策略
NOT_EMPTY:2 非空

四、公共填充字段

比如employee中 创建人,修改人,创建时间,修改时间都属于公共字段,我们没必要写到controller中,可以通过mybatispuls的公共填充机制。
在这里插入图片描述
①实体类中
在这里插入图片描述

还有一个问题就是我们自己编写的配置类没有办法获取http的请求,所以id如何获得呢?
在这里插入图片描述
解决:使用ThreadLocal
每个
每次http请求都对应一个线程。
在这里插入图片描述
在这里插入图片描述

package com.cky.common;

/**
 * @ClassName MythreadLocal
 * @Description TODO
 * @Author lukcy
 * @Date 2024/6/25 9:21
 * @Version 1.0
 */

/**
 * 线程工具类
 */
public class MythreadLocal {
    //因为存放的是id 所以是lONG
    private static ThreadLocal<Long> threadLocal=new ThreadLocal<>();

    public static void setCurrendId(Long id){
        threadLocal.set(id);
    }

    public static Long getCurrendId(){
        return threadLocal.get();
    }
}

在filter中已经登录后在threadLocal中保存id,在我们编写的元数据处理配置类中获取id。
在这里插入图片描述

package com.cky.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 java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * @ClassName MyMetaObjectHandler
 * @Description TODO
 * @Author lukcy
 * @Date 2024/6/25 9:12
 * @Version 1.0
 */
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
      metaObject.setValue("createTime", LocalDateTime.now());
      metaObject.setValue("updateTime", LocalDateTime.now());
      metaObject.setValue("createUser", MythreadLocal.getCurrendId());
      metaObject.setValue("updateUser",MythreadLocal.getCurrendId() );
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        long id = Thread.currentThread().getId();
        log.info("当前线程id{}",id);
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser",MythreadLocal.getCurrendId());
    }
}

控制器中这些就可以省略了。
在这里插入图片描述

五、文件上传与下载

5.1 拦截器与过滤器

请求处理顺序总结
前端请求拦截器:在请求发出之前处理请求。
后端过滤器:在请求到达控制器之前进行预处理。
后端拦截器:在请求到达控制器之前进行进一步的处理。
控制器:处理请求并生成响应。
后端拦截器(响应阶段):在响应返回之前处理响应。
后端过滤器(响应阶段):在响应返回之前进行进一步的处理。
前端响应拦截器:在响应返回到客户端之前处理响应。

AJAX请求:前端拦截器(如axios拦截器)会处理请求和响应,然后请求会经过后端过滤器和拦截器,最后到达控制器。
非AJAX请求:不会经过前端拦截器,而是直接发送到服务器。请求会先经过后端过滤器和拦截器,最后到达控制器。
因此,对于非AJAX请求,前端的axios拦截器(request.use 和 response.use)将不会被触发。这些请求只会经过后端的过滤器和拦截器进行处理。

写上述是因为把upload.html 加入之后,返回的是200,相应内容在这里插入图片描述
我在想,为什么这里也没有登录并且common/page 并没有在过滤器里配置,怎么没有返回到login页面。

原因:
因为emlment-upload 组件 是一个普通的http请求,该请求会经过我们的后端过滤器,所以返回了NOTLOGIN(我们在过滤器里面配置的),由于他不是一个ajax请求,所以并不会经过我们的前端拦截器的请求和相应。所以不会跳转到登录页面,我们可以看到登录页面是在前端拦截器的响应中写的。
在这里插入图片描述

六、菜品分类页面

①发送ajax请求,获取内容
②发送下载图片的请求(前端写好了,直接调用之前的下载图片的代码就行

首先想到的是最基本的代码:

    @GetMapping("/page")
    public R<Page> list(int page,int pageSize,String name){
        Page<Dish> dishPage=new Page<>(page,pageSize);


        LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(name!=null,Dish::getName,name);
        lambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);
        dishService.page(dishPage,lambdaQueryWrapper);

        return R.success(dishPage);
    }

但是我们打开页面时会发现 菜品名分类并没有显示。
理由
在这里插入图片描述

我们并没有categoryName这一列。
所以就需要用到之前的Dishdto
在这里插入图片描述
由于dishtao并没有实际对应的表,所以我们需要首先通过Dish来查到基本数据,再去填充CategoryName这一列

    @GetMapping("/page")
    public R<Page> list(int page,int pageSize,String name){
        Page<Dish> dishPage=new Page<>(page,pageSize);
        Page<DishDto> dishdtoPage=new Page<>();

        LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(name!=null,Dish::getName,name);
        lambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);
        dishService.page(dishPage,lambdaQueryWrapper);
        
        BeanUtils.copyProperties(dishPage,dishdtoPage,"records");
        
        List<Dish> records = dishPage.getRecords();
        List<DishDto> collect = records.stream().map((item) -> {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(item, dishDto);
            //通过id获取菜品分类名称
            Long categoryId = item.getCategoryId();
            Category category = categoryService.getById(categoryId);
            String name1 = category.getName();
            dishDto.setCategoryName(name1);
            return dishDto;
        }).collect(Collectors.toList());
        dishdtoPage.setRecords(collect);
        return R.success(dishdtoPage);
    }

虽然DishDto没有直接对应的数据库表,但你可以使用Dish的Mapper进行数据库操作,并在Service层或Mapper中进行数据转换和填充。这样可以保持代码的简洁和结构的清晰,同时充分利用MyBatis-Plus的功能。

七、自己实现

7.1 批量删除

批量删除记得删除菜品的同时把菜品对应的口味给删除了
DishController.java

    /**
     * 批量删除菜品  同时要把对应菜品的口味也对应删除
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String> delete(@RequestParam("ids") String ids){
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(Long::valueOf)
                .collect(Collectors.toList());
        dishService.deletewithfalvor(idList);
        return R.success("删除成功");
    }

DishService.java

    /**
     * 批量删除菜品  同时要把对应菜品的口味也对应删除
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String> delete(@RequestParam("ids") List<Long> ids){
//        List<Long> idList = Arrays.stream(ids.split(","))
//                .map(Long::valueOf)
//                .collect(Collectors.toList());
        dishService.deletewithfalvor(ids);
        return R.success("删除成功");
    }

DishServiceImpl.java

   /**
     * 删除菜品和口味
     * @param ids
     */
    @Override
    public void deletewithfalvor(List<Long> ids) {
        //删除菜品时首先要判断 是否有套餐包含该菜品 以及该菜品是否在售
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(SetmealDish::getDishId,ids);
        int count = setmealDishService.count(lambdaQueryWrapper);
        //如果有套餐包含 则抛出异常
        if(count>0){
            throw new MyException("有套餐包含该菜品 不能删除");
        }
        //如果在售 则抛出异常
        LambdaQueryWrapper<Dish> lambdaQueryWrapper1=new LambdaQueryWrapper<>();
        lambdaQueryWrapper1.in(Dish::getId,ids);
        lambdaQueryWrapper1.eq(Dish::getStatus,1);
        int count1 = this.count(lambdaQueryWrapper1);
        if(count1>0){
            throw  new MyException("有菜品正在售 不能删除");
        }

        //其他正常删除 菜品 以及菜品对应的口味表
        this.removeByIds(ids);
        LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper2=new LambdaQueryWrapper<>();
        lambdaQueryWrapper2.in(DishFlavor::getDishId,ids);
        dishFlavorService.remove(lambdaQueryWrapper2);

    }

有套餐包含菜品 就不能删除

7.2 批量起售

    /**
     * 批量起售
     * @param ids
     * @return
     */
    @PostMapping("/status/1")
    public R<String> start(@RequestParam("ids") List<Long> ids){
        LambdaUpdateWrapper<Dish> lambdaUpdateWrapper=new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.in(Dish::getId,ids);
        lambdaUpdateWrapper.set(Dish::getStatus,1);
        boolean update = dishService.update(lambdaUpdateWrapper);
        if (update) {
            return R.success("批量起售成功");
        } else {
            return R.error("批量起售成功");
        }
    }

7.3 批量停售

如果有套餐包含 该菜品 并且菜品正在售卖 则不能停售菜品

 /**
     * 批量停售 需要判断 是否有套餐包含该菜品 如果该套餐在售 则不能停售
     * @param ids
     * @return
     */
    @PostMapping("/status/0")
    public R<String> stop(@RequestParam("ids") List<Long> ids) {
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(SetmealDish::getDishId, ids); // 先把包含菜品的给选出来

        // 接着把该菜品对应的套餐选出来
        List<SetmealDish> list = setmealDishService.list(lambdaQueryWrapper);
        Set<Long> setmealIds = list.stream()
                .map(SetmealDish::getSetmealId)
                .collect(Collectors.toSet()); // 去重复

        if (!setmealIds.isEmpty()) {
            LambdaQueryWrapper<Setmeal> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
            lambdaQueryWrapper1.in(Setmeal::getId, setmealIds);
            lambdaQueryWrapper1.eq(Setmeal::getStatus, 1); // 需要确保套餐在售,套餐在售则菜品不能停售,否则可以

            int count = setmealService.count(lambdaQueryWrapper1);
            if (count > 0) {
                throw new MyException("有套餐包含该菜品,正在售");
            }
        }

        LambdaUpdateWrapper<Dish> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(Dish::getId, ids);
        updateWrapper.set(Dish::getStatus, 0); // 修改状态为停售

        boolean update = dishService.update(updateWrapper);
        if (update) {
            return R.success("状态修改成功");
        } else {
            return R.error("状态修改失败");
        }
    }

八 、套餐

8,1 保存套餐和其对应的菜品信息

 /**
     * 保存套餐和其对应的菜品信息
     * @param setmealDto
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody SetmealDto setmealDto){
        //保存套餐和其对应的菜品信息
        setmealService.savewithdishes(setmealDto);
        return R.success("套餐保存成功");
    }
  //保存套餐和其对应的菜品信息
    @Override
    public void savewithdishes(SetmealDto setmealDto) {
        this.save(setmealDto);//保存套餐信息
        //获得套餐对应的菜品信息 并为其赋值(套餐id
        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
        List<SetmealDish> collect = setmealDishes.stream().map((item) -> {
            item.setSetmealId(setmealDto.getId());
            return item;
        }).collect(Collectors.toList());
        setmealDishService.saveBatch(collect);//批量保存

    }

8.2 套餐分页展示

 /**
     * 对套餐信息进行分页展示
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> list(Integer page,Integer pageSize,String name){
        log.info("{},{},{}",page,pageSize,name);
        //首先先获取套餐的内容
        Page<Setmeal> setmealPage=new Page<>();
        LambdaQueryWrapper<Setmeal> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.like(name!=null,Setmeal::getName,name);
        lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);
        setmealService.page(setmealPage,lambdaQueryWrapper);
        //由于我们页面展示的还有套餐所属套餐分类的名称,所以需要dto
        Page<SetmealDto> setmealDtoPage=new Page<>();
        //属性赋值 除了records这一列
        BeanUtils.copyProperties(setmealPage,setmealDtoPage,"records");
        //获取原始套餐的信息
        List<Setmeal> records = setmealPage.getRecords();
        //遍历原始套餐的信息并为其赋上套餐分类的名称
        List<SetmealDto> collect = records.stream().map((item) -> {
            SetmealDto setmealDto = new SetmealDto();
            BeanUtils.copyProperties(item, setmealDto);
            Long categoryId = item.getCategoryId();
            Category category = categoryService.getById(categoryId);
            setmealDto.setCategoryName(category.getName());
            return setmealDto;
        }).collect(Collectors.toList());
        setmealDtoPage.setRecords(collect); //这一步一定要写 不然没有内容
        return R.success(setmealDtoPage);
    }

8.3 删除套餐

套餐如果在售 则不能删除

 /**
     * 套餐删除
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String> delete(@RequestParam List<Long> ids){
        log.info("{}",ids);
        setmealService.deletewithdish(ids);
        return R.success("删除 成功");
    }
  /**
     * // 删除套餐和其对应的套餐菜品表
     * @param ids
     */
    @Transactional
    @Override
    public void deletewithdish(List<Long> ids) {
        //查询要删除的套餐是否有在售的
        LambdaQueryWrapper<Setmeal> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(Setmeal::getId,ids);
        lambdaQueryWrapper.eq(Setmeal::getStatus,1);
        int count = this.count(lambdaQueryWrapper);


        //如果有抛出一个异常
        if(count>0){
            throw new MyException("选择的套餐有在售的 不能删除");
        }
        //如果没有 则先删除套餐
        this.removeByIds(ids);

        //接着删除setmeal——dish表

        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper1=new LambdaQueryWrapper<>();
        lambdaQueryWrapper1.in(SetmealDish::getSetmealId,ids);

        setmealDishService.remove(lambdaQueryWrapper1);
    }

8.4 根据菜品类型获得所有菜品

  /**
     * 根据菜品的分类id获得所有菜品
     * @param dish
     * @return
     */
    @GetMapping("/list")
    public R<List<Dish>> listR(Dish dish)
    {
        Long id = dish.getCategoryId();
        LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(id!=null,Dish::getCategoryId,id);
        lambdaQueryWrapper.eq(Dish::getStatus,1);
        lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
        List<Dish> list = dishService.list(lambdaQueryWrapper);
        return R.success(list);

    }

8.5 修改套餐 (自己实现

①首先发送了ajax请求,对所选择的套餐进行回显。

②点击保存,发送ajax请求,对套餐内容进行保存。

 /**
     *回显套餐内容
     * @param id
     * @return
     */
    @GetMapping("{id}")
    public R<SetmealDto> showandupdate(@PathVariable Long id){
        //回显套餐内容进行修改
        SetmealDto updatewithdishes = setmealService.get(id);
        return R.success(updatewithdishes);
    }

    @PutMapping
    public R<String> update(@RequestBody SetmealDto setmealDto){
        setmealService.updatewithdiehes(setmealDto);
        return R.success("修改套餐信息成功");

    }
package com.cky.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.cky.dto.SetmealDto;
import com.cky.pojo.Setmeal;

import java.util.List;

/**
 * @ClassName SetmealService
 * @Description TODO
 * @Author lukcy
 * @Date 2024/6/25 20:02
 * @Version 1.0
 */
public interface SetmealService extends IService<Setmeal> {
// //保存套餐和其对应的菜品信息
    void savewithdishes(SetmealDto setmealDto);
// 删除套餐和其对应的套餐菜品表
    void deletewithdish(List<Long> ids);

    SetmealDto get(Long id);
    void updatewithdiehes(SetmealDto setmealDto);
}

  /**
     * 回显套餐内容
     * @param id
     */
    @Override
    public SetmealDto get(Long id) {
        SetmealDto setmealDto=new SetmealDto();
        Setmeal setmeal1 = setmealService.getById(id);
        BeanUtils.copyProperties(setmeal1,setmealDto);
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SetmealDish::getSetmealId,id);//获得套餐对应的菜品
        List<SetmealDish> list = setmealDishService.list(lambdaQueryWrapper);

        setmealDto.setSetmealDishes(list);//将菜品信息也放入setmealdto
        return setmealDto;


    }

    /**
     * 修改套餐信息
     * @param setmealDto
     */
    @Override
    public void updatewithdiehes(SetmealDto setmealDto) {
        //修改套餐的基本信息
        this.updateById(setmealDto);
        //清除原来套餐的菜品
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SetmealDish::getSetmealId,setmealDto.getId());
        setmealDishService.remove(lambdaQueryWrapper);
        //更新新的菜品
        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
        setmealDishes.stream().map((item->{
            item.setSetmealId(setmealDto.getId());
            return item;
        })).collect(Collectors.toList());
        setmealDishService.saveBatch(setmealDishes);

    }

九、菜品

9.1 菜品展示

首先登录进入之后,共同发送了两个ajax请求,①获得所有分类并展示,②获取第一个分类的所有菜品。

由于我们在添加购物车时,需要选择相应的规格即口味信息,所以我们要对list方法进行一个重新编写,返回菜品的同时能够返回口味信息。

    /**
     * 根据菜品的分类id获得所有菜品
     * @param dish
     * @return
     */
    @GetMapping("/list")
    public R<List<DishDto>> listR(Dish dish)
    {
        Long id = dish.getCategoryId();
        LambdaQueryWrapper<Dish> lambdaQueryWrapper=new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(id!=null,Dish::getCategoryId,id);
        lambdaQueryWrapper.eq(Dish::getStatus,1);
        lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
        List<Dish> list = dishService.list(lambdaQueryWrapper);//获得所有的菜品 但是为了让前端能够展示规格 我们需要将口味也返回
        List<DishDto> listdto=null;
        List<DishDto> collect = list.stream().map((item) -> {
            DishDto dishDto = new DishDto();
            BeanUtils.copyProperties(item, dishDto);
            //通过id获取菜品分类名称
            Long categoryId = item.getCategoryId();
            Category category = categoryService.getById(categoryId);
            String name1 = category.getName();
            dishDto.setCategoryName(name1);

            //根据菜品id获得 口味信息
            Long dishId = item.getId();
            LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper=new LambdaQueryWrapper<>();
            dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
            List<DishFlavor> list1 = dishFlavorService.list(dishFlavorLambdaQueryWrapper);
            dishDto.setFlavors(list1);
            return dishDto;
        }).collect(Collectors.toList());
        return R.success(collect);

    }

这并不会影响我们之前后端的程序,因为我们只是多添加了一些属性,但是原来的dish信息都还在。

9.2 添加购物车

    /**
     * 添加购物车  前端就没有给可以多个口味的选择 只要选择了一个口味 就只能点击加号 选择同样的口味了
     * 如果可以添加不同的口味 那么number就要变了 因为不能仅仅通过菜品id来number加1  如果口味不同 那就属于不同的
     * @param shoppingCart
     * @return
     */
    @PostMapping("/add")
    public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
        //设置userid
        shoppingCart.setUserId(MythreadLocal.getCurrendId());
        //查看相同菜品或者套餐是否在数据库中 如果有直接分数加1
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId,MythreadLocal.getCurrendId());
        lambdaQueryWrapper.eq(shoppingCart.getDishId()!=null,ShoppingCart::getDishId, shoppingCart.getDishId());
        lambdaQueryWrapper.eq(shoppingCart.getSetmealId()!=null,ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
        ShoppingCart serviceOne = shoppingCartService.getOne(lambdaQueryWrapper);
        if (serviceOne!=null){
            Integer number = serviceOne.getNumber();
            serviceOne.setNumber(number+1);
            serviceOne.setCreateTime(LocalDateTime.now());
            shoppingCartService.updateById(serviceOne);
        }
        else {
            //没有查到
            shoppingCart.setNumber(1);
            shoppingCart.setCreateTime(LocalDateTime.now());
            shoppingCartService.save(shoppingCart);
            serviceOne=shoppingCart;
        }
        return R.success(serviceOne);
    }

9.3 减少菜品或套餐数量

 /**
     * 减少套餐或者菜品
     * @param shoppingCart
     * @return
     */
    @PostMapping("/sub")
    public  R<ShoppingCart> sub(@RequestBody ShoppingCart shoppingCart){

        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId,MythreadLocal.getCurrendId());
        lambdaQueryWrapper.eq(shoppingCart.getSetmealId()!=null,ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
        lambdaQueryWrapper.eq(shoppingCart.getDishId()!=null,ShoppingCart::getDishId,shoppingCart.getDishId());

        ShoppingCart serviceOne = shoppingCartService.getOne(lambdaQueryWrapper);
        Integer number = serviceOne.getNumber();
        if(number>=2){
            serviceOne.setNumber(number-1);
            shoppingCartService.updateById(serviceOne);
        }
        else
            shoppingCartService.removeById(serviceOne);
        return R.success(serviceOne);

    }

9.4 查看购物车

  /**
     * 查看购物车
     * @return
     */
    @GetMapping("/list")
    public R<List<ShoppingCart>> list(){
        log.info("查看购物车");
        Long currendId = MythreadLocal.getCurrendId();
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId,currendId);
        lambdaQueryWrapper.orderByDesc(ShoppingCart::getCreateTime);

        List<ShoppingCart> list = shoppingCartService.list(lambdaQueryWrapper);
        return R.success(list);
    }

9.5 清空购物车

** /**
     * 清空购物车
     * @return
     */
    @DeleteMapping("/clean")
    public R<String> clean(){

        Long currendId = MythreadLocal.getCurrendId();
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId,currendId);//
        shoppingCartService.remove(lambdaQueryWrapper);
        return R.success("清空购物车成功");
    }**
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值