【瑞吉外卖day03】

分类管理业务

1.编辑员工信息

1.1 需求分析

在这里插入图片描述

1.2 代码开发

在这里插入图片描述
点击新增员工按钮,F12调试器调试,带add.html的请求url如下:
在这里插入图片描述
员工信息查询请求url如下:
在这里插入图片描述

整个修改员工信息过程分两步:
一是用员工id查询员工信息,并在编辑页面进行回显;
二是编辑完成后,点击保存按钮,执行update操作。

根据上面的请求url,编写第一步的代码,也就是用id去查询员工信息:

@GetMapping("/{id}")
    public R<Employee> getEmpById(@PathVariable Long id){
        log.info("根据id查询员工信息");
        Employee empbyid = employeeService.getById(id);
        if(empbyid!=null){
            return R.success(empbyid);
        }
        return R.error("没有查询到对应员工信息");

    }

测试:发现员工信息回显成功。
在这里插入图片描述
在这里插入图片描述
看前端的js我们会发现,前端调用ajax请求queryEmployeeById后,本质就是把res.data传给ruleForm对象。而sex由于后端传来的是“0”或“1”的标志位,前端要进行特殊处理。我们也可以查看前端的ajax请求queryEmployeeById的代码:
在这里插入图片描述
在这里插入图片描述
那么下一步需要编写update方法吗?其实已经不需要了,因为我们前面在编写用户启用/禁用功能时,已经写好了update方法,点击保存之后本质上还是调用了那个方法,所以其实现在功能已经完成了。
测试发现编辑员工功能正常运行。

2.公共字段维护

2.1 需求分析

在这里插入图片描述

2.2 代码实现

在这里插入图片描述
给Employee实体类的几个需要自动填充的属性加上**@TableField注解**。
在这里插入图片描述
在这里插入图片描述
我们在common包下编写一个类MyMetaObjectHandler,实现MetaObjectHandler接口。
注意要加上**@Component注解**

@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 插入时自动填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insert]...");
        log.info(metaObject.toString());

    }

    /**
     * 更新时自动填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]...");
        log.info(metaObject.toString());
    }
}

这个类目前没有做任何处理,只是输出日志。
我们将之前增加员工controller的公共字段赋值的语句删去,然后debug后输出:
在这里插入图片描述
发现四个公共字段值为null。
重启系统测试后,发现报错,原因是,数据库中这四个字段设置为了非空
在这里插入图片描述
我们在insertFill和updateFill中对字段进行填充,由于在本类中获取不到session,因此我们暂时将createUser和UpdateUser写死。后续完善。

@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(1));
        metaObject.setValue("updateUser",new Long(1));
    }
    /**
     * 更新时自动填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]...");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("updateUser",new Long(1));
    }
}

公共字段维护的好处是,无论是员工,还是菜品,还是订单,只要包含了这几个公共字段,我们都可以自动进行填充。
测试,运行:添加李四员工正常。
在这里插入图片描述
但是目前仍然存在问题:createUser和updateUser设置的用户id是固定的值,我们需要改成动态获取当前登录用户的id。
在这里插入图片描述
解决方案是使用ThreadLocal给当前线程绑定一个登录用户id值。为什么这样可以呢?原因如下:
在这里插入图片描述
我们在登录过滤器、编辑员工controller方法、更新时自动填充方法分别日志打印当前线程id,然后在前端操作编辑员工,输出结果如下:
在这里插入图片描述
由此说明,客户端进行一次操作,也就是给服务端发送一个http请求,服务端会分配一个线程来处理,处理过程中涉及到的方法都属于同一个线程!
在这里插入图片描述
在这里插入图片描述
我们在common包下,新建基于ThreadLocal的封装工具类BaseContext,作用是保存和获取当前线程的用户id

/**
 * 基于ThreadLocal的封装工具类,作用是保存和获取当前线程的用户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();
    }
}

然后在登录拦截器中使用BaseContext设置用户id
在这里插入图片描述
然后在公共字段自动填充方法里面获取id。
在这里插入图片描述
测试运行,能成功更新createUser和updateUser字段。

3.新增分类

3.1需求分析

在这里插入图片描述
在这里插入图片描述
数据模型如下:其中type字段为1则为菜品分类,为2则为套餐分类。
在这里插入图片描述
注意:name字段数据库设置为了唯一
在这里插入图片描述

3.2 代码开发

在这里插入图片描述

首先是Category实体类

@Data
public class Category {

  private Long id;
  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;

  private static final long serialVersionUID = 1L;

}

接下来是CategoryMapper(接口)

@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}

然后是CategoryService(接口)

public interface CategoryService extends IService<Category> {
}

接下来是CategoryServiceImpl

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}

最后是CategoryController

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

架子搭起来了,没有具体功能。
在这里插入图片描述
编写新增分类方法:

@PostMapping()
    public R<String> addNewCategory(@RequestBody Category category){
        if(category!=null){
            categoryService.save(category);
            return R.success("添加成功");
        }else {
            return R.error("输入为空,请重新输入");
        }
    }

测试,成功添加一条分类。

如果此时我们继续添加同名的分类,由于数据库表中添加了分类名称的唯一性约束,所以会进入我们之前写过的全局异常处理类GlobalExceptionHandler,会向我们提示记录已经存在。这也是全局异常处理的好处。

4.分类信息分页查询

4.1 需求分析

在这里插入图片描述

4.2 代码开发

在这里插入图片描述
为什么每次进入页面就能直接看到数据了呢?看前端代码就知道,钩子函数调用init,再调用getCategoryPage
在这里插入图片描述
在这里插入图片描述

@GetMapping("/page")
    public R<Page> pageQuery(int page,int pageSize){
        //分页构造器
        Page<Category> pageInfo=new Page<>(page,pageSize);
        //条件构造器
        LambdaQueryWrapper<Category> queryWrapper=new LambdaQueryWrapper<>();
        //添加排序条件,按照sort字段升序排序
        queryWrapper.orderByAsc(Category::getSort);
        //执行查询
        categoryService.page(pageInfo,queryWrapper);

        return R.success(pageInfo);

    }

测试:正常运行
在这里插入图片描述
为什么我们传给前端的type是数字,而前端能显示中文呢?因为前端做了转换。
在这里插入图片描述

5.删除分类

5.1 需求分析

在这里插入图片描述

5.2 代码开发

在这里插入图片描述编写controller代码

@DeleteMapping
    public R<String> delCategory(Long ids){
        log.info("删除的分类id:{}",ids);
        categoryService.removeById(ids);
        return R.success("分类信息删除成功");

    }

测试,删除成功
但是我们这里有问题:当分类已经关联了菜品或者套餐时,此分类不容许删除
在这里插入图片描述
接下来建套餐、菜品的实体类、mapper、service

Dish实体类:

@Data
public class Dish {

  private Long id;
  private String name;
  private Long categoryId;
  private BigDecimal price;
  private String code;
  private String image;
  private String description;
  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;
  private Integer isDeleted;

  private static final long serialVersionUID = 1L;
}

Setmeal实体类:

@Data
public class Setmeal {

  private Long id;
  private Long categoryId;
  private String name;
  private BigDecimal price;
  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;
  private Integer isDeleted;


  private static final long serialVersionUID = 1L;
}

这两个类的mapper、service根据之前的套路建即可。

接下来开始具体实现删除分类业务,我们先在CategoryService中写个remove方法:

public interface CategoryService extends IService<Category> {
    /**
     * 删除分类
     * @param id
     */
    public void remove(Long id);
}

然后在CategoryServiceImpl中实现,编写具体逻辑:

@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> dishQueryWrapper=new LambdaQueryWrapper<>();
        //添加查询条件:分类id
        dishQueryWrapper.eq(Dish::getCategoryId,id);
        int count=dishService.count(dishQueryWrapper);

        if(count>0){
            //已经关联菜品,抛出业务异常
            throw new CustomException("当前分类关联了套餐,不能删除");
        }

        //查询当前分类是否关联套餐,如果有,抛出业务异常
        LambdaQueryWrapper<Setmeal> setmealQueryWrapper=new LambdaQueryWrapper<>();
        //添加查询条件:分类id
        setmealQueryWrapper.eq(Setmeal::getCategoryId,id);
        int count1=setmealService.count(setmealQueryWrapper);

        if(count1>0){
            //已经关联套餐,抛出业务异常
            throw new CustomException("当前分类关联了套餐,不能删除");
        }
        //正常删除分类
        super.removeById(id);
    }
}

当我们的当前分类已经关联菜品或者套餐时,则抛出业务异常。我们需要在common新建一个自定义异常类CustomException,继承RuntimeException(运行时异常)

public class CustomException extends RuntimeException{
    public CustomException(String message) {
        super(message);
    }
}

然后我们在全局异常处理类GlobalExceptionHandler中增加方法,通过这样的方式,可以给用户返回具体异常信息。

/**
     * 自定义异常处理
     * @param ce
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public R<String> exceptionHandler(CustomException ce){
        log.info(ce.getMessage());
        return R.error(ce.getMessage());
    }

测试运行,发现关联了套餐的分类点击删除时弹出提示信息,说明功能能正常运行。
在这里插入图片描述

6.修改分类

6.1 需求分析

在这里插入图片描述
回显操作不用我们自己完成,前端已经完成了。

6.2 代码开发

编写controller

/**
     * 根据id修改分类
     * @return
     */
    @PutMapping
    public R<String> updateCategory(@RequestBody Category category){
        categoryService.updateById(category);
        return R.success("分类修改成功");
    }

测试,修改功能正常

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值