🌈个人主页 :一条泥憨鱼 (欢迎各位大佬莅临)

今天与大家一同学习苍穹外卖day03(菜品管理)。
公共字段自动填充:
这是一个偏技术而非业务的需求
问题分析和实现思路:

我们想要在调用到某些操作类型的时候就对公共字段进行自动填充(更新)

先创建一个包,将所有注解放在这个包下面,并枚举操作类型



接下来创建切面


在Mapper方法上加入注解

JoinPoint 参数
- 作用:封装了当前连接点(被拦截方法)的信息
对于以下四个字段,使用update和insert方法时要重新赋值的字段不同

切面设计代码完整版
java
@Aspect //加这个注解声明是切面
@Component //本质仍是bean对象,要交给spring管理
@Slf4j //方便日志记录
public class AutoFillAspect {
//切面由切入点和通知(用于代码增强)组成
//切入点
@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
//execution内为要拦截的地方
public void autoFillPointCut() {
}
//前置通知,进行公共字段的填充
@Before("autoFillPointCut()")
public void autoFill(JoinPoint joinPoint) {
log.info("开始进行公共字段填充..");
//获取到当前拦截到的方法上的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
OperationType operationType = autoFill.value();//获得数据库操作类型
//获取到当前被拦截方法的参数--实体对象
Object[] args = joinPoint.getArgs();
if (args.length == 0 || args == null) {
return;
}
Object entity = args[0];
//准备为字段赋值的数据
LocalDate now = LocalDate.now();
Long currentId = BaseContext.getCurrentId();
//根据当前不同的操作类型,为对应的属性通过反射来获取
if (operationType == OperationType.INSERT) {
try {
Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象赋值
setCreateTime.invoke(entity, now);
setCreateUser.invoke(entity, currentId);
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (operationType == OperationType.UPDATE) {
//为2个公共字段赋值
try {
Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//通过反射为对象赋值
setUpdateTime.invoke(entity, now);
setUpdateUser.invoke(entity, currentId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
拦截方法
↓
获取方法签名
↓
获取@AutoFill注解
↓
获取操作类型(INSERT/UPDATE)
↓
获取方法参数
↓
获取实体对象
↓
准备填充数据
↓
通过反射填充字段
在categoryMapper中也应该加上注解

至此,我们已经可以通过设计的切面对象实现公共字段自动填充
所以在service的impl里面的save和update方法就不需要再保留为公共字段赋值的部分了
我们直接将其注释掉
以后只需要在Mapper里的方法上加注解就可以了

新增菜品------需求分析和设计
业务规则

接口设计

根据类型查询分类,这里因为是新增菜品,所以请求参数统一用1

文件上传

新增菜品

数据库设计
文件上传接口设计
根据接口文档的名称以及命名规范,我们要新建一个Controller类,Common表示通用,新增菜品结束,后续也可能会用上

controller层代码的实现(阿里云OSS的启动需要回顾JavaWeb148集的知识)


- CommonController的upload方法主要是调用AliOssUtil(阿里云OSS工具类)来上传文件
- 文件上传到云存储后,返回的是文件访问路径
- 这个操作不涉及数据库操作,所以不需要Mapper层
- 由于业务逻辑很简单(只是调用工具类上传),所以也省略了Service层
不是所有功能都需要Service和Mapper层,这是合理的架构设计:
- 工具类操作(如文件上传、短信发送、邮件发送):直接使用工具类
- 业务逻辑操作(如用户管理、订单处理):使用Service + Mapper层
- 第三方服务调用(如支付接口、地图服务):直接调用API或使用工具类
新增菜品接口设计
在controller层新增dishcontroller,代码设计如下

service层代码设计

返回改善controller层代码

在service层声明方法

impl层的实现

后续Mapper接口和xml文件的代码实现都比较简单,这里就不多解释了
菜品分页查询
需求分析和设计

注意:categoryName字段是来自category表

代码开发
由于categoryName不是来自菜品表,所以要设计VO,使用分类名称字段,使得前端能正常展示数据

接下来在controller层编写代码


爆红是因为service层并无此方法,所以我们去service层实现该方法

然后我们去impl实现类实现此方法。既然是分页查询,那就又要用上我们的PageHelper了
java
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO){
PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
Page<DishVO> page=dishMapper.pageQuery(dishPageQueryDTO);
return new PageResult(page.getTotal(),page.getResult());
}
对应xml文件的代码如下
在接口文档进行调试的时候,如果状态码爆401,记得去员工登录处获取新token
删除菜品
需求分析和设计
我们把删除一个也算进批量删除中

接口设计

数据库设计
这里需要操作的数据库较多

代码开发

controller层
加RequestParam注解表示由MVC框架自动将参数转化为一个集合
(注意:这里括号内是Long不是long)

在service层扩展方法

在impl层具体实现
java
//菜品批量删除
@Transactional
public void deleteBatch(List<Long> ids){
//判断当前菜品是否能够删除---是否存在起售中菜品
for (Long id : ids) {
Dish dish=dishMapper.getById(id);
if(dish.getStatus()== StatusConstant.ENABLE){
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//判断当前菜品是否能够删除---是否被套餐关联
List<Long> setmealIds=setmealDishMapper.getSetmealIdsByDishIds(ids);
if(setmealIds!=null&&setmealIds.size()>0){
//当前套餐被关联了,不能删除
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
//删除菜品表中的菜品数据
for (Long id : ids) {
dishMapper.deleteById(id);
//删除菜品关联的口味数据
dishFlavorMapper.deleteByDishId(id);
}
}
后续Mapper层和xml文件的实现也都比较常规,不多赘述
修改菜品
需求分析和设计



代码实现
根据id查询菜品

controller层(爆红处记得传入参数id,这里主包失误了,后面才发现)

service层

service实现层

mapper层方法的实现

修改菜品

controller

service

service实现层

mapper层

由于这里比较复杂,所以我们去xml文件里编写动态sql

至此,修改菜品的代码开发我们已经实现完毕

