[自用,更新自day5]瑞吉外卖代码及笔记

文章目录

day1

数据库环境搭建

导入表结构,命令行中: (注意目录中不要有中文字符)

可以直接拖动文件进去

mysql 复制代码
mysql> source D:\db\xxx

配置WebMvcConfig

如果不放到static文件夹下,是无法访问到我们的静态资源的。

此时可以通过这个配置类进行静态资源映射。

注意点:

@Configuration 声明是配置类 且要放在config文件夹下

config文件夹与启动类同级

java 复制代码
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport{
    /**
     * 配置静态资源访问
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("配置静态资源访问");
        registry.addResourceHandler("/backend/**").
                addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").
                addResourceLocations("classpath:/front/");
    }
}

弹幕:

Dao是ssm基于jdbc的,需要在impl中实现具体函数。Mapper是Mybatis的,只需要接口映射xml就可以

java 复制代码
//controller
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;
}


//service
public interface EmployeeService extends IService<Employee> {

}

//serviceImpl
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {

}
/*
1. 实现了 EmployeeService 接口。这意味着该类需要提供接口中定义的所有方法的具体实现
2. 通过继承 ServiceImpl ,以使用 MyBatis-Plus 提供的通用服务方法
3. ServiceImpl 是 MyBatis-Plus 提供的一个基础实现类,包含了对数据库的基本 CRUD 操作
*/



//mapper
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {

}
/*
1. Mapper注解 允许 MyBatis 自动生成实现类来执行 SQL 操作
2. BaseMapper 是 MyBatis-Plus 提供的一个通用 Mapper 接口,获得了一系列的 CRUD 方法(如 insert, delete, update, selectById 等),这些方法与 Employee 实体类相对应。
*/

返回结果类

java 复制代码
@Data
public class R<T> {

    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }
}

登录/退出登录

java 复制代码
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeService employeeService;

    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {

        String password = employee.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());

        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername, employee.getUsername());
        Employee emp = employeeService.getOne(queryWrapper);

        if (emp == null) {
            return R.error("用户名不存在");
        }
        if (!emp.getPassword().equals(password)) {
            return R.error("密码错误");
        }

        if(emp.getStatus()==0){
            return R.error("账号已被禁用");
        }
        request.getSession().setAttribute("employee", emp);
        return R.success(emp);
    }

    @PostMapping("/logout")
    public R<String>logout(HttpServletRequest request){
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }
}

day2

完善登录功能

实现步骤:

1.自定义过滤器

2.在启动类上加入注解@ServletComponentScan

3.完善过滤器

java 复制代码
package com.itheima.reggie.filter;
import java.io.IOException;

import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.R;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import jakarta.servlet.ServletException;
import org.springframework.util.AntPathMatcher;

@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    //路径匹配器,支持通配符
    public static final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //向下转型
        //浏览器传进来的参数是HttpServletRequest类型,而ServletRequest是HttpServletRequest的父类
        //我们用这个父类接收,所以要向下转型,才能使用子类的方法
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse  response= (HttpServletResponse) servletResponse;

        String requestURI = request.getRequestURI();

        log.info("requestURI:{}",requestURI);

        //2.定义不需要处理的路径
        String[] urls = {
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/frontend/**",
        };
        //3.如果不需要处理,则直接放行
        if(checkUrl(requestURI, urls)){
            filterChain.doFilter(request, response);
            return;
        }
        //4.如果需要处理,则判断是否登录
        Object employee = request.getSession().getAttribute("employee");
        if(employee!=null){
            log.info("已登录,用户信息:{}",employee);
            filterChain.doFilter(request, response);
            return;
        }
        //5.如果没有登录,则通过输出流向客户端页面响应数据
        log.info("未登录,拦截请求:{}",requestURI);
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }
    public boolean checkUrl(String requestURI, String[] urls) {
        for (String url : urls) {
            if (pathMatcher.match(url, requestURI)) {
                return true;
            }
        }
        return false;
    }
}

新增员工

java 复制代码
//GlobalExceptionHandler.java
//全局异常处理器

@ControllerAdvice(annotations = {RestController.class, Controller.class})//无论是Controller还是RestController都会被拦截
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String>exceptionHandler(SQLIntegrityConstraintViolationException e){
        log.error(e.getMessage());
        if(e.getMessage().contains("Duplicate entry")){
            String[] split = e.getMessage().split(" ");
            String msg =split[2]+"已存在";
            return R.error(msg);
        }

        return R.error("未知错误");
    }
}
java 复制代码
//EmployeeController.java
@PostMapping
    public R<String> save(HttpServletRequest request, @RequestBody Employee employee) {
       log.info("employee:{}", employee.toString());
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());
        //获得当前登录用户id
        Object emp = request.getSession().getAttribute("employee");
        Long empId = ((Employee) emp).getId();
        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);
        employeeService.save(employee);
        return R.success("保存成功");
 }

分页查询员工信息

java 复制代码
//EmployeeController.java
@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(name!=null,Employee::getName,name);
        //添加排序条件
        queryWrapper.orderByDesc(Employee::getUpdateTime);
        employeeService.page(pageInfo,queryWrapper);
        //执行查询
        return R.success(pageInfo);
    }
java 复制代码
//MybatisPlusConfig
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

启用/禁用员工信息

问题:前端js处理数字最多存16位,导致前段返回的Id与数据库不一致

解决方法:服务端用消息转换器

具体实现步骤:

1)提供对象转换器JacksonObjectMapper,基于jackson进行Java对象到json数据的转换

java 复制代码
//一个工具类
/**
 * 对象映射器:基于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配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行java对象到json数据的转换

java 复制代码
//JaksonObjectMapper.java

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
@Component
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);
    }
}
java 复制代码
//WebMvcConfig.java
/**
     * 扩展mvc框架的消息转换器
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展mvc框架的消息转换器");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转换为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器添加到mvc框架的消息转换器集合中
        converters.add(0,messageConverter);//index=0说明是优先使用

    }

弹幕里面的其他方法:

1.在实体类中的id上添加注解:@JsonSerialize(using=ToStringSerializer.class)

2.在实体类中的id上添加注解:

@JsonFormat(shape=JsonFormat.Shape.STRING)

编辑员工信息

编辑和新增共用一个页面 只要新增一个返回员工信息的接口就可以了

java 复制代码
//EmploeeController.java
@GetMapping("/{id}")
    public R<Employee> findById(@PathVariable Long id){
        log.info("根据Id查询员工信息,id:{}",id);
        Employee employee = employeeService.getById(id);
        if(employee==null){
            return R.error("员工不存在");
        }
        return R.success(employee);
    }

day3

公共字段自动填充

创建人、创建时间、修改时间、修改人是公共字段

Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。

实现步骤:

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

java 复制代码
  @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;

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

问题:怎么在这个元数据对象处理器接口获取当前用户id

ThreadLocal类

在学习ThreadLocal之前,我们需要先确认一个事情,就是客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中涉及到下面类中的方法都属于相同的一个线程:

1、LoginCheckFilter的doFilter方法

2、Employeecontroller的update方法

3、MyMetaObjectHandler的updaeFill方法

可以在上面的三个方法中分别加入下面代码(获取当前线程id):

java 复制代码
long id = Thread.currentThread().getId():
log.info("线程id:{}".id);

执行编辑员工功能进行验证,通过观察控制台输出可以发现,一次请求对应的线程id是相同的:

  • 什么是ThreadLocal?

  • ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本

  • ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。

  • ThreadLocal常用方法:

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

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

实现步骤:

实现步骤:

1、编写BaseContext工具类,基于ThreadLocal封装的工具类

java 复制代码
package com.itheima.reggie.common;

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

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

java 复制代码
        Employee employee = (Employee) request.getSession().getAttribute("employee");


        if(employee!=null){
            BaseContext.setCurrentId(employee.getId());
            log.info("已登录,用户信息:{}",employee);
            filterChain.doFilter(request, response);
            return;
        }

3、在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

java 复制代码
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insertFill]");
        metaObject.setValue("createTime", LocalDateTime.now()) ;
        metaObject.setValue("updateTime", LocalDateTime.now()) ;
        metaObject.setValue("createUser", BaseContext.getCurrentId()) ;
        metaObject.setValue("updateUser", BaseContext.getCurrentId()) ;
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[updateFill]");
        metaObject.setValue("updateTime", LocalDateTime.now()) ;
        metaObject.setValue("updateUser", BaseContext.getCurrentId()) ;
    }
}

新增分类

java 复制代码
 @PostMapping
    public R<String> save(@RequestBody Category category) {
        log.info("保存分类信息:{}", category);
        categoryService.save(category);
        return R.success("保存成功");
    }

分类信息分页查询

java 复制代码
@GetMapping("/page")
    public R page(int page,int pageSize) {
       Page<Category> pageInfo = new Page<>(page, pageSize);
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByAsc(Category::getSort);
        categoryService.page(pageInfo, queryWrapper);
        return R.success(pageInfo);
    }

删除分类

java 复制代码
//Contoller
@DeleteMapping
    public R<String> delete(Long ids) {
        log.info("删除分类信息:{}", ids);
        categoryService.removeById(ids);
        return R.success("删除成功");
}
//Service
public interface CategoryService extends IService<Category> {
    public void remove(Long id);
}
//ServiceImpl
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService{

    @Autowired
    private DishService dishService;

    @Autowired
    private SetmealService setmealService;

    @Override
    public void remove(Long id) {
        LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
        dishLambdaQueryWrapper.eq(Dish::getCategoryId, id);
        int count1 = (int) dishService.count(dishLambdaQueryWrapper);
        if (count1 > 0) {
            throw new RuntimeException("该分类下有菜品,不能删除");
        }
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId, id);
        int  count2 = (int) setmealService.count(setmealLambdaQueryWrapper);
        if(count2 > 0){
            throw new RuntimeException("该分类下有套餐,不能删除");
        }

    }
}

修改分类

java 复制代码
  @PutMapping
    public R<String> update(@RequestBody Category category) {
        log.info("更新分类信息:{}", category);
        categoryService.updateById(category);
        return R.success("更新成功");
    }

day4

文件上传下载

文件上传

  • 服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件:
    • commons-fileupload
    • commons-io
  • Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明-个MultipartFile类型的参数即可接收上传的文件,例如:

文件下载

  • 文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程

  • 通过浏览器进行文件下载,通常有两种表现形式:

    • 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
    • 直接在浏览器中打开

    通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。

java 复制代码
import org.springframework.beans.factory.annotation.Value;
@Value("${reggie.path}")
//注意导的是这个包
java 复制代码
//CommonController
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {
    //file是一个临时文件,需要转存到指定位置,否则本次请求完成后,文件会被删除

    @Value("${reggie.path}")
    private String basePath;

    //上传文件
    @PostMapping("/upload")
    public R<String> upload(MultipartFile file) {
        log.info("上传文件:{}", file.getOriginalFilename());
        String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
        //使用UUID生成文件名
        String fileName = UUID.randomUUID().toString() + suffix;


        File dir = new File(basePath);
        if(!dir.exists()) {
            dir.mkdirs();
        }

        try {
            //将文件保存到指定位置
            file.transferTo(new File(basePath + fileName));

        }catch (Exception e) {
            log.error("上传文件失败", e.getMessage());

        }
        return R.success(fileName);
    }

    //下载文件
    @GetMapping("/download")
    public void download(String name, HttpServletResponse response) {
        //输入流 读取文件
       try{
              FileInputStream fis = new FileInputStream(basePath + name);
              ServletOutputStream os = response.getOutputStream();
              response.setContentType("image/jpeg");
              byte[] bytes = new byte[1024];
              int len = 0;
                while((len = fis.read(bytes)) != -1) {
                    os.write(bytes, 0, len);
                    os.flush();
                }
                //关闭资源
                fis.close();
                os.close();
       }catch (Exception e) {
           log.error("下载文件失败", e.getMessage());
       }
    }

}

新增菜品

DTO,全称为Data Transfer Object,即数据传输对象,一般用于展示层与服务层之间的数据传输。

java 复制代码
//DishDto
@Data
public class DishDto extends Dish {
    private List<DishFlavor> flavors = new ArrayList<>();
    private String categoryName;
    private Integer copies;
}
java 复制代码
//DishController
@RequestMapping("/dish")
@RestController
@Slf4j
public class DishController {
    @Autowired
    private DishService dishService;
    

    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        log.info("dishDto:{}",dishDto.toString());
        dishService.saveWishFlavour(dishDto);
        return R.success("新增菜品成功");
    }
}
java 复制代码
//DishService
public interface DishService extends IService<Dish> {
    //新增菜品 同时插入菜品对应的口味数据 要操作两张表:dish、dish_flavor
    public void saveWishFlavour(DishDto dishDto);
}
java 复制代码
//DishServiceImpl
@Service
public class DishServiceImpl  extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;
    @Override
    @Transactional
    public void saveWishFlavour(DishDto dishDto) {
        //新增菜品 同时插入菜品对应的口味数据 要操作两张表:dish、dish_flavor
        this.save(dishDto);//调用 ServiceImpl 类中的 save 方法
        Long dishId = dishDto.getId();
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors.stream().map((item)->{
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());
        dishFlavorService.saveBatch(flavors);
    }

}

菜品信息分页查询

因为在分页查询的Dish的records(菜品记录中),只有这个菜品所属的categoryId,但是我们需要分页的时候展示的是菜品名字。

又因为DishDto里面有分类名称,所以改成返回的是DishDto

java 复制代码
 @GetMapping("/page")
    public R<Page> page(int page,int pageSize,String name){
        Page<Dish> pageInfo = new Page<>(page,pageSize);//存储原始菜品Dish的分页信息
        Page<DishDto> dishDtoPage = new Page<>(page,pageSize);//存储转换后的菜品分页信息
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(name!=null,Dish::getName,name);//如,则查询果 name 不为空,则查询
        queryWrapper.orderByDesc(Dish::getCreateTime);
        dishService.page(pageInfo,queryWrapper);
        //对象拷贝
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");//将pageInfo中的属性 除了records复制到dishDtoPage中 
        List<Dish> records = pageInfo.getRecords();//获取菜品记录列表
        //将每个Dish转换为DishDto
        /*
        1.创建一个新的DishDto示例
        2.复制Dish的属性到DishDto
        3.根据categoryId来获取相应的Category对象,并设置名称到DishDto中
        */
        List<DishDto> list =  records.stream().map((item)->{
            DishDto dishDto = new DishDto();// 1
            BeanUtils.copyProperties(item,dishDto);// 2
            Long categoryId = item.getCategoryId(); //3
            Category category = categoryService.getById(categoryId);
            String categoryName = category.getName();
            dishDto.setCategoryName(categoryName);
            return dishDto;
        }).collect(Collectors.toList());
        dishDtoPage.setRecords(list);//将转换后的列表设置到dishDtoPage的记录中
        return R.success(dishDtoPage);
    }

修改菜品

  • 单个菜品信息回显
    • 查询dish
    • 把dish转换为dishDto
    • 通过菜品id 查询dishFlavor
    • 把口味加到dishDto上
java 复制代码
//controller
 @GetMapping("/{id}")
    public R<DishDto> get(@PathVariable Long id){
        DishDto dishDto = dishService.getByIdWithFlavor(id);
        return R.success(dishDto);
    }

//service
 public DishDto getByIdWithFlavor(Long id);

//serviceImpl
public DishDto getByIdWithFlavor(Long id) {
        Dish dish = this.getById(id);

        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish,dishDto);

        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dish.getId());
        List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(flavors);
        return dishDto;
    }
  • 修改
    • 首先直接更新dish表的基本信息
    • 然后把当前口味表直接全部删除 再添加回去
java 复制代码
//controller
 @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        log.info("dishDto:{}",dishDto.toString());
        dishService.updateWithFalvor(dishDto);
        return R.success("修改菜品成功");
    }

//Service
public void updateWithFalvor(DishDto dishDto);

//ServiceImpl
 @Override
    public void updateWithFalvor(DishDto dishDto) {
        //更新dish表基本信息
        this.updateById(dishDto);
        //清理当前菜品对应口味数据 dish_flavor表的delete操作
        dishFlavorService.remove(new LambdaQueryWrapper<DishFlavor>().eq(DishFlavor::getDishId,dishDto.getId()));
        //添加当前提交的口味数据 dish_flavor表的insert操作
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item)->{
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);
    }

day5

新增套餐

将新增页面录入的套餐信息插入到setmeal表,还要向setmeal_dish表插入套餐和菜品关联数据,所以新增的时候涉及到两个表。

1.展示菜品分类

java 复制代码
  @GetMapping("/list")
    public R<List<Dish>> list(Dish dish){
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Dish::getStatus,1);
        queryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
        queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
        List<Dish> list = dishService.list(queryWrapper);
        return R.success(list);
    }
java 复制代码
@Override
public void saveWithDish(SetmealDto setmealDto) {
    //保存套餐信息 操作setmeal表 执行insert操作
    this.save(setmealDto);
    List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
    setmealDishes.forEach(item -> item.setSetmealId(setmealDto.getId()));
    //保存套餐和菜品的关系 操作setmeal_dish表 执行insert操作
    setmealDishService.saveBatch(setmealDishes);
}

分页查询

java 复制代码
 @GetMapping("/page")
    public R<Page> page(int page,int pageSize,String name){
        //分页构造器对象
        Page<Setmeal> pageInfo = new Page<>(page,pageSize);
        Page<SetmealDto> dtoPage = new Page<>();

        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件,根据name进行like模糊查询
        queryWrapper.like(name != null,Setmeal::getName,name);
        //添加排序条件,根据更新时间降序排列
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);

        setmealService.page(pageInfo,queryWrapper);

        //对象拷贝
        BeanUtils.copyProperties(pageInfo,dtoPage,"records");
        List<Setmeal> records = pageInfo.getRecords();

        List<SetmealDto> list = records.stream().map((item) -> {
            SetmealDto setmealDto = new SetmealDto();
            //对象拷贝
            BeanUtils.copyProperties(item,setmealDto);
            //分类id
            Long categoryId = item.getCategoryId();
            //根据分类id查询分类对象
            Category category = categoryService.getById(categoryId);
            if(category != null){
                //分类名称
                String categoryName = category.getName();
                setmealDto.setCategoryName(categoryName);
            }
            return setmealDto;
        }).collect(Collectors.toList());

        dtoPage.setRecords(list);
        return R.success(dtoPage);
    }

删除套餐

java 复制代码
    @Override
    @Transactional
    public void removeWithDish(List<Long> ids) {
        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Setmeal::getId, ids);
        queryWrapper.eq(Setmeal::getStatus, 1);
        long count = this.count(queryWrapper);
        if(count>0) {
            throw new RuntimeException("删除的套餐中存在已上架的套餐,不能删除");
        }
        //删除套餐和菜品的关系 操作setmeal_dish表 执行delete操作
        this.removeByIds(ids);
        setmealDishService.remove(new LambdaQueryWrapper<SetmealDish>().in(SetmealDish::getSetmealId, ids));
    }
相关推荐
大筒木老辈子1 小时前
Linux笔记---协议定制与序列化/反序列化
网络·笔记
草莓熊Lotso1 小时前
【C++】递归与迭代:两种编程范式的对比与实践
c语言·开发语言·c++·经验分享·笔记·其他
我爱挣钱我也要早睡!4 小时前
Java 复习笔记
java·开发语言·笔记
汇能感知9 小时前
摄像头模块在运动相机中的特殊应用
经验分享·笔记·科技
阿巴Jun9 小时前
【数学】线性代数知识点总结
笔记·线性代数·矩阵
茯苓gao9 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾9 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT10 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
ST.J10 小时前
前端笔记2025
前端·javascript·css·vue.js·笔记
Suckerbin11 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全