【黑马】瑞吉外卖-Day03、04笔记

14 篇文章 0 订阅
5 篇文章 0 订阅

瑞吉外卖Day03、04

公共字段自动填充

使用MybatisPlus实现

问题分析

请添加图片描述

代码实现

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

实现步骤:

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

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

对公共的字段加入注解,指定其自动填充的策略

    @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中的公共字段手动填充注释掉

功能完善

前面我们已经完成了公共字段自动填充功能的代码开发,但是还有一个问题没有解决,就是我们在自动填充createUser和updateUser时设置的用户id是固定值,现在我们需要改造成动态获取当前登录用户的id。
有的同学可能想到,用户登录成功后我们将用户id存入了HttpSession中,现在我从HttpSession中获取不就行了?
注意,我们在MyMetaObjectHandler类中是不能获得HttpSession.对象的,所以我们需要通过其他方式来获取登录用户id。可以使用ThreadLocals来解决此问题,它是)DK中提供的一个类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zag7YbYn-1673267797811)(瑞吉外卖Day03、04.assets/image-20230107140339052.png)]

什么是ThreadLocal?

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

ThreadLocal常用方法:

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

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

实现步骤:

  1. 编写BaseContext.工具类,基于ThreadLocal封装的工具类
  2. 在LoginCheckFilterl的doFilter,方法中调用BaseContext:来设置当前登录用户的id:
  3. 在MyMetaObjectHandler的方法中调用BaseContext获取登录用户
/**
 * 自定义元数据对象处理器
 */
@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",new Long(BaseContext.getCurrentId()));
        metaObject.setValue("updateUser",new Long(BaseContext.getCurrentId()));
    }

    /**
     * 更新自动填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段填充-UPDATE");
       // log.info(metaObject.toString());
       // log.info("当前线程的id:{}",Thread.currentThread().getId());
        metaObject.setValue("updateTime",LocalDateTime.now());

        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }
}
/**
 * 基于ThreadLocak封装工具类,用户保存和获取当前session中的id
 * 作用范围为一个线程之内,一次请求一个线程
 */
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();
    }
}

新增分类功能

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fOUJe6uU-1673267797812)(瑞吉外卖Day03、04.assets/image-20230107143233970.png)]

数据模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s5wGbRwm-1673267797813)(瑞吉外卖Day03、04.assets/image-20230107143400391.png)]

代码开发

在开发业务功能前,先将需要用到的类和接口基本结构创建好:

  • 实体类Category(直接从课程资料中导入即可)

  • Mapper接口CategoryMapper

  • 业务层接口CategoryService

  • 业务层实现类CategoryServicelmpl

  • 控制层CategoryController

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4Ccl0KK-1673267797813)(瑞吉外卖Day03、04.assets/image-20230107144619885.png)]

@RestController
@Slf4j
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    /**
     * 新增分类
     * @param category
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody Category category){
        log.info("新增菜品为:{}",category.toString());
        categoryService.save(category);
        return R.success("新增菜品成功");
    }
}

分类信息分页查询

代码开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yeS582hg-1673267797813)(瑞吉外卖Day03、04.assets/image-20230107145724393.png)]

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

    }

删除分类功能

需求分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8oLYRhox-1673267797813)(瑞吉外卖Day03、04.assets/image-20230107175652691.png)]

代码开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIaiCKXS-1673267797814)(瑞吉外卖Day03、04.assets/image-20230107175731098.png)]

功能完善

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wml8s2EL-1673267797814)(瑞吉外卖Day03、04.assets/image-20230107181403360.png)]

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    @Autowired
    private DishService dishService;
    @Autowired
    private SetmealService setmealService;
    /**
     * 删除分类,删除之前要进行判断
     * @param id
     */
    @Override
    public void remove(Long id) {
        LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
        dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);
        int count = dishService.count(dishLambdaQueryWrapper);
        //查询当前的分类是否关联了菜品,如果关联直接抛出异常
        if (count > 0){
            //抛出异常
            throw  new CustomException("当前分类下关联了菜品,无法删除");
        }
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);
        int count1 = setmealService.count(setmealLambdaQueryWrapper);

        //查询当前分类是否关联了套餐,如果关联直接抛出异常
        if (count1 > 0){
            throw  new CustomException("当前分类下关联了套餐,无法删除");
        }
        // 如果判断都没有成功,那么直接删除
        super.removeById(id);
    }
}
    /**
     * 根据ID删除分类
     * @param id
     * @return
     */
    @DeleteMapping
    public R<String> delete(Long id){
        log.info("删除分类的id:{}",id);
        categoryService.remove(id);
        return R.success("删除成功");
    }

自定义异常

/**
 * 自定义业务异常
 */
public class CustomException extends RuntimeException{
    public CustomException(String message){
        super(message);
    }
}

将自定义异常添加到全局异常处理类当中,这样就可以在前端页面提示具体的错误信息

@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException exception){
    log.info(exception.getMessage());
    return R.error(exception.getMessage());//页面上就会显示提示信息
}

修改分类

需求分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfqofoM9-1673267797814)(瑞吉外卖Day03、04.assets/image-20230107184154124.png)]

    /**
     * 修改分类信息
     * @param category
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody Category category){
        log.info("修改分类信息:{}",category.toString());
        categoryService.updateById(category);
        return R.success("修改分类信息成功");
    }

文件上传下载功能

文件上传介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGj8WF4d-1673267797814)(瑞吉外卖Day03、04.assets/image-20230107185729369.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQTON0Fr-1673267797815)(瑞吉外卖Day03、04.assets/image-20230107185815338.png)]

文件下载介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EQvTje11-1673267797815)(瑞吉外卖Day03、04.assets/image-20230107190055623.png)]

文件上传代码实现

@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {
    @Value("${reggie.path}")
    private String basePath;

    @PostMapping("/upload")
    //file名字要和前端name里面的名字一样
    public R<String> upload(MultipartFile file){
        // file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件将会删除
        log.info("上传文件");
        // 新的文件名字
        String fileName = UUID.randomUUID().toString()+
                file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
        // 创建一个目录文件
        File dir = new File(basePath);
        if (!dir.exists()){
            dir.mkdirs();
        }
        try {

            file.transferTo(new File(basePath+fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.success(fileName);
    }
}
reggie:
  path: D:\reggieimg\

文件下载代码实现

    /**
     * 文件下载
     * @param name
     * @param response
     */
    @GetMapping("/download")
    public void dowanload(String name, HttpServletResponse response){

        try {
            // 输入流来读取文件内容
            FileInputStream inputStream = new FileInputStream(new File(basePath+name));
            // 输出流将文件写回浏览器
            ServletOutputStream outputStream = response.getOutputStream();
            // 设置相应文件的格式
            response.setContentType("image/jepg");
            int len = 0;
            // 将读到的数据写入到byte数组中
            byte[] bytes = new byte[1024];
            while ((len = inputStream.read(bytes)) != -1){
                // 如果len不等于-1,那就继续读,直到len=-1,表示已经读取完毕
                // 将读取到的数据通过输出流写回浏览器
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }

            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

新增菜品功能

需求分析

后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
在这里插入图片描述

数据模型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-14Qn1ljj-1673267797815)(瑞吉外卖Day03、04.assets/image-20230108123618696.png)]

代码开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UUWfPgKJ-1673267797816)(瑞吉外卖Day03、04.assets/image-20230108124025620.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qlm9PAuG-1673267797816)(瑞吉外卖Day03、04.assets/image-20230108130539201.png)]

导入DTO

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QcXV8MR1-1673267797816)(瑞吉外卖Day03、04.assets/image-20230108132344945.png)]

菜品下拉框的实现

/**
 * 菜品下拉框
 * @param category
 * @return
 */
@GetMapping("/list")
public R<List<Category>> list(Category category){
    log.info("菜品分类下拉框.");
    LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
    //添加条件
    queryWrapper.eq(category.getType() != null,Category::getType,category.getType());
    //添加排序条件
    queryWrapper.orderByAsc(Category::getSort);
    queryWrapper.orderByDesc(Category::getUpdateTime);
    //通过MybatisPlus使用list方法查询到所有符合条件的菜品,并封装为list传递给前端
    List<Category> list = categoryService.list(queryWrapper);
    return R.success(list);
}

传输的数据和实体类的属性并不是一一对应,需要引入DTO对象来进行传输

@Data
public class DishDto extends Dish {

    private List<DishFlavor> flavors = new ArrayList<>();

    private String categoryName;

    private Integer copies;
}

在service新增一个方法来将菜品和菜品口味存储到不同的表中

@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;
    /**
     * 新增菜品和口味数据
     * @param dishDto
     */
    @Override
    @Transactional
    public void saveWithFlavor(DishDto dishDto) {
        // 保存菜品到菜品表
        this.save(dishDto);
        // 保存菜品口味数据到口味表
        Long dishId = dishDto.getId();

        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item)->{
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);

    }
}

涉及到多张表,这里面需要开始事务操作,并且在启动类中加入@EnableTransactionManagement注解开启事务

@RestController
@Slf4j
@RequestMapping("/dish")
public class DishController {
    @Autowired
    private DishService dishService;

    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        dishService.saveWithFlavor(dishDto);
        return R.success("新增菜品成功..");
    }
}

菜品信息分页查询

与之前不同的是,需要展示菜品的图片和分类

代码开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AIsDJYug-1673267797817)(瑞吉外卖Day03、04.assets/image-20230108141024440.png)]

    /**
     * 分页条件查询
     * @param page
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public R<Page> page(int page, int pageSize, String name){
        //分页构造器
        Page<Dish> pageInfo = new Page<Dish>(page,pageSize);
        Page<DishDto> dishDtoPage = new Page<>();

        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        //添加过滤条件
        queryWrapper.like(name != null,Dish::getName,name);

        //增加排序条件
        queryWrapper.orderByDesc(Dish::getUpdateTime);
        dishService.page(pageInfo,queryWrapper);
        //进行对象拷贝,将上面的page拷贝到下面的page中去
        BeanUtils.copyProperties(pageInfo,dishDtoPage,"records");
        List<Dish> records = pageInfo.getRecords();
        //基于stream流的方式,通过lambda表达式,将records中的属性拷贝到Dishdto对象中,并且通过service层查询到categotyId所对应的
        //分类名称,设置到DishDto对象当中,返会一个DishDto的list对象到前端页面
        List<DishDto> list = records.stream().map((item)->{
            DishDto dishDto = new DishDto();
            Long categoryId = item.getCategoryId();//分类id
            String categoryName = categoryService.getById(categoryId).getName();
            dishDto.setCategoryName(categoryName);
            BeanUtils.copyProperties(item,dishDto);
            return dishDto;
        }).collect(Collectors.toList());
        dishDtoPage.setRecords(list);
        return R.success(dishDtoPage);
    }

修改菜品

需求分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyyT9yEl-1673267797817)(瑞吉外卖Day03、04.assets/image-20230109201308389.png)]

代码开发

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQQf72hw-1673267797817)(瑞吉外卖Day03、04.assets/image-20230109201339823.png)]

    /**
     * 根据菜品id查询菜品和口味信息
     * @param id
     * @return
     */
    @Override
    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查询出来
        List<DishFlavor> list = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(list);
        return dishDto;
    }

    /**
     * 更新菜品和口味信息
     * @param dishDto
     */
    @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        // 更新菜品表
        this.updateById(dishDto);

        // 清理当前菜品表的口味数据-delete
        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());

        dishFlavorService.remove(queryWrapper);
        // 再添加口味味表信息-insert

        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors.stream().map((item)->{
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);
    }
    // 请求在url中
    /**
     * 根据id进行查询菜品信息并回显
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> getById(@PathVariable Long id){
        DishDto byIdWithFlavor = dishService.getByIdWithFlavor(id);
        return R.success(byIdWithFlavor);
    }

    /**
     * 更新菜品
     * @param dishDto
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        dishService.updateWithFlavor(dishDto);
        return R.success("修改菜品成功");
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小七rrrrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值