整理记录下学习整个瑞吉外卖项目,详细代码可在我的Gitee仓库瑞吉外卖实战克隆下载学习使用!
在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工信息时也要设置修改时间和修改人。这些字段属于公共字段,同时很多表也存在,如图
使用MyBatisPlus的公共字段填充功能,在填充字段或更新的时候为指定字段进行赋予指定值,另外对于存在session中的用户id,可以用ThreadLocal(Thread局部变量)来解决,每次Http请求发送后后台会创建唯一的线程来执行对应操作,且ThreadLocal会给线程提供单一的存储空间,线程内才能访问,获取到值,可通过set()和get()来设置和获取当前线程对应的局部变量值。
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();}
}
@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()); }
}
新添加用户,如图
测试成功,数据正确,如图
可以在后台系统的分类管理页面分别添加菜品和套餐分类,如图
将窗口新增的数据插入到category表中,表结构如图
@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;
}
@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("新增分类成功"); }
}
添加川菜信息, 如图
点击确定后显示已存在,说明功能完成
和之前员工分页查询类似,代码如下:
/*** 分页查询* @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);}
在管理分类页面,可以对某个分类进行删除操作,但当分类关联了菜品或套餐时,此分类不允许被删除
/** 菜品 */
@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;
}
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); }
}
当删除川菜时会提示删不了,如图
@PutMapping
public Result update(@RequestBody Category category){ log.info("修改分类信息:{}",category); categoryService.updateById(category); return Result.success("修改分类信息成功");
}
文件上传用到Apache下两组件:commons-fileupload和commons-io,接收参数为MultipartFile,下载有以附件形式下载,弹出保存对话框并将文件保存到指定磁盘和直接在浏览器中打开两种方式。本质为服务端以文件流的形式写回到浏览器的过程。
@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); }}
@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(); }
}
新增菜品就是将新增的菜品直接插入到dish表中,若有口味则直接向dish_flavor表插入数据,涉及到两个表:
- dish表 菜品表
- dish_flavor 菜品口味表
@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;
}
@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);
}
@Data
public class DishDto extends Dish { //菜品对应的口味数据 private List flavors = new ArrayList<>(); private String categoryName; private Integer copies;
}
@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);
}
添加菜品信息断点调试意料之中,如图
@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);
}
刷新菜品展示页面,需要把下载好的图片放进之前在yml创建的图片路径中,图片才能正常显示,如图
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中添加方法如下:
@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动态数组