瑞吉外卖项目-菜品类别与套餐类别添加和修改以及查询,菜品的添加修改等功能
创始人
2024-06-02 16:17:08
0

整理记录下学习整个瑞吉外卖项目,详细代码可在我的Gitee仓库瑞吉外卖实战克隆下载学习使用!

7. 分类管理

7.1 公共字段填充

7.1.1问题

在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工信息时也要设置修改时间和修改人。这些字段属于公共字段,同时很多表也存在,如图在这里插入图片描述

7.1.2 解决办法

使用MyBatisPlus的公共字段填充功能,在填充字段或更新的时候为指定字段进行赋予指定值,另外对于存在session中的用户id,可以用ThreadLocal(Thread局部变量)来解决,每次Http请求发送后后台会创建唯一的线程来执行对应操作,且ThreadLocal会给线程提供单一的存储空间,线程内才能访问,获取到值,可通过set()和get()来设置和获取当前线程对应的局部变量值。

7.1.3 代码开发

  • 编写BaseContext工具类,使用ThreadLocal保存和获取用户id
	public class BaseContext {private static ThreadLocal threadLocal = new ThreadLocal<>();/*** 设置值* @param id*/public static void setCurrentId(Long id){threadLocal.set(id);}/*** 获取值* @return*/public static Long getCurrentId(){return threadLocal.get();}
}	
  • 实体类属性加上@TableField注解,指定自动填充策略,如图![[Pasted image 20230301113501.png]]
  • 按框架要求编写元数据对象处理器,在此类中进行统一赋值,需实现MetaObjectHandler接口
@Component  
@Slf4j  
public class MyMetaObjecthandler implements MetaObjectHandler {  /**  * 插入操作,自动填充  * @param metaObject  */  @Override  public void insertFill(MetaObject metaObject) {  log.info("公共字段自动填充[insert]...");  log.info(metaObject.toString());  metaObject.setValue("createTime", LocalDateTime.now());  metaObject.setValue("updateTime",LocalDateTime.now());  metaObject.setValue("createUser",BaseContext.getCurrentId());  metaObject.setValue("updateUser",BaseContext.getCurrentId());  }  /**  * 更新操作,自动填充  * @param metaObject  */  @Override  public void updateFill(MetaObject metaObject) {  log.info("公共字段自动填充[update]...");  log.info(metaObject.toString());  long id = Thread.currentThread().getId();  log.info("线程id为:{}",id);  metaObject.setValue("updateTime",LocalDateTime.now());  metaObject.setValue("updateUser",BaseContext.getCurrentId());  }  
}
  • 在登录过滤器代码中使用BaseContext设置id,如图
    ![[Pasted image 20230301152907.png]]
  • 将之前controller中写的设置createTime等公共字段值注释掉,如图
    ![[Pasted image 20230301152756.png]]

7.1.4 测试

新添加用户,如图![[Pasted image 20230301153202.png]]
测试成功,数据正确,如图![[Pasted image 20230301153329.png]]

7.2 新增分类

7.2.1 需求分析

![[Pasted image 20230301153458.png]]
可以在后台系统的分类管理页面分别添加菜品和套餐分类,如图
![[Pasted image 20230301153603.png]]
![[Pasted image 20230301153615.png]]

7.2.2 数据模型

将窗口新增的数据插入到category表中,表结构如图在这里插入图片描述

7.2.3 代码开发

  • 实体类编写,也可进行导入,如下:
@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;
}
  • 和之前员工功能一样,新增controller、mapper层和业务层接口及实现类,如图
    ![[Pasted image 20230301155354.png]]
  • controller层添加代码,完成添加菜品功能,如下:
@RestController  
@RequestMapping("/category")  
@Slf4j  
@RequiredArgsConstructor  
public class CategoryController {  private final CategoryService categoryService; /**  * 新增分类  * @param category  * @return  */  @PostMapping  public Result save(@RequestBody Category category){  log.info("category:{}",category);  categoryService.save(category);  return Result.success("新增分类成功");  }
}

7.2.4 测试

添加川菜信息, 如图![[Pasted image 20230301161329.png]]
点击确定后显示已存在,说明功能完成,![[Pasted image 20230301161412.png]]

7.3 分类信息分页查询

7.3.1代码开发

和之前员工分页查询类似,代码如下:

    /*** 分页查询* @param page* @param pageSize* @return*/@GetMapping("/page")public Result page(int page,int pageSize){//分页构造器Page pageInfo = new Page<>(page,pageSize);//条件构造器LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();//添加排序条件,根据sort进行排序queryWrapper.orderByAsc(Category::getSort);//分页查询categoryService.page(pageInfo,queryWrapper);return Result.success(pageInfo);}

7.4 删除分类

7.4.1需求分析

在管理分类页面,可以对某个分类进行删除操作,但当分类关联了菜品或套餐时,此分类不允许被删除

7.4.2 代码开发

  • controller层代码加入删除函数,如图![[Pasted image 20230301162837.png]]
  • 由于有些分类涉及到菜品或套餐,这里直接添加或复制菜品类和套餐类实体代码,如下:
/**  菜品  */  
@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;  
}
/**  * 套餐  */  
@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;  
}
  • 同样,添加对应的Mapper,Service接口及实现类
  • 添加自定义异常类,来处理分类下已关联套餐或菜品无法删除的异常,代码如下:
public class CustomException extends RuntimeException{  public CustomException(String message){  super(message);  }  
}
  • 全局异常处理类中添加自定义异常类,如下:
@ExceptionHandler(CustomException.class)  
public Result exceptionHandler(CustomException ex){  log.error(ex.getMessage());  return Result.error(ex.getMessage());  
}
  • 分类接口实现类重写删除方法,如下:
@Service  
@RequiredArgsConstructor  
public class CategoryServiceImpl extends ServiceImpl implements CategoryService {  private final DishService dishService;  private final SetmealService setmealService;  /**  * 根据id删除分类,删除之前需要进行判断  * @param id  */  @Override  public void remove(Long id) {  LambdaQueryWrapper dishLambdaQueryWrapper = new LambdaQueryWrapper<>();  //添加查询条件,根据分类id进行查询  dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);  int dishCounts = (int) dishService.count(dishLambdaQueryWrapper);  //查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常  if(dishCounts > 0){  //已经关联菜品,抛出一个业务异常  throw new CustomException("当前分类下关联了菜品,不能删除");  }  //查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常  LambdaQueryWrapper setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();  //添加查询条件,根据分类id进行查询  setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);  int setmealCount = (int) setmealService.count(setmealLambdaQueryWrapper); if(setmealCount > 0){  //已经关联套餐,抛出一个业务异常  throw new CustomException("当前分类下关联了套餐,不能删除");  }  //正常删除分类  super.removeById(id);  }  
}

7.4.3 测试

当删除川菜时会提示删不了,如图![[Pasted image 20230301165247.png]]

7.5 修改分类

  • 和之前一样,controller层编写如下代码:
@PutMapping  
public Result update(@RequestBody Category category){  log.info("修改分类信息:{}",category);  categoryService.updateById(category);  return Result.success("修改分类信息成功");  
}

8.菜品管理

8.1.文件上传下载

8.1.1 介绍

文件上传用到Apache下两组件:commons-fileupload和commons-io,接收参数为MultipartFile,下载有以附件形式下载,弹出保存对话框并将文件保存到指定磁盘和直接在浏览器中打开两种方式。本质为服务端以文件流的形式写回到浏览器的过程。

8.1.2 代码开发

  • 文件上传,在application.yml加入本地路径,并编写控制层,如下:
@RestController  
@ResponseBody  
@Slf4j  
@RequestMapping("/common")  
public class CommonController {  @Value("${fileUpload.path}")  private String filePath;  @PostMapping("/upload")  public Result fileUpload(MultipartFile file) {  //file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除  log.info(file.toString());  
//        获取原来文件名称  String originalFilename = file.getOriginalFilename();  
//        文件后缀名  String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));  String fileName = UUID.randomUUID().toString() + suffix;  File fileFoldersPath = new File(filePath + fileName);  try {  
//        若文件路径不存在则创建  if(!fileFoldersPath.exists()){  fileFoldersPath.mkdirs();  }  file.transferTo(fileFoldersPath);  } catch (IOException e) {  e.printStackTrace();  }  return Result.success(fileName);  }}
  • 文件下载,添加操作代码到controller层,如下:
@GetMapping("/download")  
public void downLoad(String name, HttpServletResponse response){  try {  //输入流,通过输入流读取文件内容  FileInputStream fileInputStream = new FileInputStream( new File(filePath + name));  response.setContentType("image/jpeg");  //输出流,通过输出流将文件写回浏览器  ServletOutputStream outputStream = response.getOutputStream();  int len = 0;  byte[] bytes = new byte[1024];  while((len = fileInputStream.read(bytes)) != -1){  outputStream.write(bytes,0,len);  outputStream.flush();  }  outputStream.close();  fileInputStream.close();  } catch (Exception e) {  e.printStackTrace();  }  
}
  • 在LoginFilter开放权限,防止拦截,如图![[Pasted image 20230301215621.png]]

8.2 新增菜品

8.2.1 需求分析

![[Pasted image 20230301194916.png]]

8.2.2 数据模型

新增菜品就是将新增的菜品直接插入到dish表中,若有口味则直接向dish_flavor表插入数据,涉及到两个表:
- dish表 菜品表
- dish_flavor 菜品口味表
![[Pasted image 20230301195615.png]]
![[Pasted image 20230301195640.png]]

8.2.3 代码开发

  • 菜品实体类、对应的Mapper,业务层接口及实现类前面已写好,这里仅写菜品口味实体类对应的代码,实体类如下,其它类似上文:
@Data  
public class DishFlavor implements Serializable {  private static final long serialVersionUID = 1L;  private Long id;  //菜品id  private Long dishId;  //口味名称  private String name;  //口味数据list  private String value;  @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;  
}
  • 编写根据条件查询菜品类别数据,在CategoryController中加入如下代码
@GetMapping("/list")  
public Result> list(Category category){  //条件构造器  LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();  //添加条件  queryWrapper.eq(category.getType() != null,Category::getType,category.getType());  //添加排序条件  queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);  List list = categoryService.list(queryWrapper);  return Result.success(list);  
}
  • 新增数据传输类DishDTO,将Dish类数据和DishFlavors结合起来与前台进行交互,代码如下:
@Data  
public class DishDto extends Dish {  //菜品对应的口味数据  private List flavors = new ArrayList<>();  private String categoryName;  private Integer copies;  
}
  • 新增DishController类,添加保存菜品功能,代码如下:
@RestController  
@RequestMapping("/dish")  
@Slf4j  
@RequiredArgsConstructor  
public class DishController {  private final DishService dishService;  private final DishFlavorService dishFlavorService;  private final CategoryService categoryService;//新增菜品@PostMapping  
public Result save(@RequestBody DishDto dishDto) {  log.info(dishDto.toString());  dishService.saveWithFlavor(dishDto);  return Result.success("新增菜品成功");  
}
}
  • 编写菜品口味添加方法到接口,并重写方法如下:
@Transactional  
public void saveWithFlavor(DishDto dishDto) {  //保存菜品的基本信息到菜品表dish  this.save(dishDto);  Long dishId = dishDto.getId();//菜品id  //菜品口味  List flavors = dishDto.getFlavors();  flavors = flavors.stream().map((item) -> {  item.setDishId(dishId);  return item;  }).collect(Collectors.toList());  //保存菜品口味数据到菜品口味表dish_flavor  dishFlavorService.saveBatch(flavors);  
}

8.2.4 测试

添加菜品信息断点调试意料之中,如图
![[Pasted image 20230301225830.png]]

8.3.菜品信息分页查询

8.3.1 代码开发

  • 要求和之前一样,但数据包含两个实体信息,需要多表联查或拼接查询,这里用MyBatisPlus进行操作,controller层代码如下:
@GetMapping("/page")  
public Result page(int page, int pageSize, String name) {  //构造分页构造器对象  Page pageInfo = new Page<>(page, pageSize);  Page dishDtoPage = new Page<>();  //条件构造器  LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();  //添加过滤条件  queryWrapper.like(name != null, Dish::getName, name);  //添加排序条件  queryWrapper.orderByDesc(Dish::getUpdateTime);  //执行分页查询  dishService.page(pageInfo, queryWrapper);  //对象拷贝  BeanUtils.copyProperties(pageInfo, dishDtoPage, "records");  List records = pageInfo.getRecords();  List list = records.stream().map((item) -> {  DishDto dishDto = new DishDto();  BeanUtils.copyProperties(item, dishDto);  Long categoryId = item.getCategoryId();//分类id  //根据id查询分类对象  Category category = categoryService.getById(categoryId);  if (category != null) {  String categoryName = category.getName();  dishDto.setCategoryName(categoryName);  }  return dishDto;  }).collect(Collectors.toList());  dishDtoPage.setRecords(list);  return Result.success(dishDtoPage);  
}

8.3.2 测试

刷新菜品展示页面,需要把下载好的图片放进之前在yml创建的图片路径中,图片才能正常显示,如图![[Pasted image 20230301232109.png]]

8.4 修改菜品

8.4.1 代码开发

  • 编写业务层,根据id找菜品及口味并更新菜品口味,如图
    ![[Pasted image 20230304152057.png]]
    实现重写方法,如下:
public DishDto getByIdWithFlavor(Long id) {  //查询菜品基本信息,从dish表查询  Dish dish = this.getById(id);  DishDto dishDto = new DishDto();  BeanUtils.copyProperties(dish,dishDto);  //查询当前菜品对应的口味信息,从dish_flavor表查询  LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();  queryWrapper.eq(DishFlavor::getDishId,dish.getId());  List flavors = dishFlavorService.list(queryWrapper);  dishDto.setFlavors(flavors);  return dishDto;  
}
@Transactional  
public void updateWithFlavor(DishDto dishDto) {  //更新dish表基本信息  this.updateById(dishDto);  //清理当前菜品对应口味数据---dish_flavor表的delete操作  LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();  queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());  dishFlavorService.remove(queryWrapper);  //添加当前提交过来的口味数据---dish_flavor表的insert操作  List flavors = dishDto.getFlavors();  flavors = flavors.stream().map((item) -> {  item.setDishId(dishDto.getId());  return item;  }).collect(Collectors.toList());  dishFlavorService.saveBatch(flavors);  
}
  • 编写controller层,如图![[Pasted image 20230304152211.png]]

8.4.2 测试

运行后点击修改菜品,如图测试成功
![[Pasted image 20230304145226.png]]
点击修改,如图
![[Pasted image 20230304145159.png]]

8.5 起售或停用

8.5.1 需求分析

菜品可能因为材料不足无法进行烹饪,所以对上面的菜品进行停售操作,防止造成损失,当材料齐全后可以起售

8.5.2 代码开发

在controller中添加方法如下:

@PostMapping("/status/{status}")  public Result status(@PathVariable Integer status, @RequestParam List ids) {  log.info("status:{},ids:{}", status, ids);  LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>();  updateWrapper.in(ids != null, Dish::getId, ids);  updateWrapper.set(Dish::getStatus, status);  LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>();  lambdaQueryWrapper.in(Dish::getId, ids);  List dishes = dishService.list(lambdaQueryWrapper);  for (Dish dish : dishes) {  String key = "dish_" + dish.getCategoryId() + "_1";  }  dishService.update(updateWrapper);  return Result.success("批量操作成功");  }

上一篇:C#和.net框架之第一弹

下一篇:C动态数组

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...