苍穹外卖|DAY3-菜品管理的公共字段填充和新增菜品模块
目录七、菜品管理1. 公共字段自动填充1.1 问题分析1.2 实现思路1.3 代码开发1.4 功能测试2. 新增菜品2.1 需求分析和设计2.2 代码开发2.2.1 开发文件上传接口2.2.2 新增菜品2.3 功能测试八、小tip七、菜品管理1. 公共字段自动填充1.1 问题分析在业务表中 如创建人、创建时间、修改人、修改时间等公共字段 在维护这些数据时 都需要为这些字段赋值 在程序中就会存在很多重复代码注代码冗余 不便于后期维护1.2 实现思路创建人、创建人id在执行insert语句时需要赋值修改时间、修改人id在执行insert、update语句时需要赋值思路(1)自定义注解AutoFill 用于表示需要进行公共字段自动填充 的方法(2)自定义切面类AutoFillAspect 统一拦截加入了AutoFill注解的方法 通过反射为公共字段赋值(3)在Mapper的方法上加入AutoFill注解表明当前方法需要被拦截 需要为这几个公共字段来赋值技术点枚举、注解、AOP、反射1.3 代码开发获取当前被拦截方法的参数时 约定实体对象为方法的第一个参数 这样每次获取第一个参数即可通过常量的方法引用方法名 更规范且不易出错AutoFillpackage com.sky.annotation; import com.sky.enumeration.OperationType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定义注解用于标识某个方法需要进行功能字段自动填充处理 */ Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface AutoFill { //数据库操作类型UPDATE INSERT OperationType value(); }AutoFillAspectpackage com.sky.aspect; import com.sky.annotation.AutoFill; import com.sky.constant.AutoFillConstant; import com.sky.context.BaseContext; import com.sky.enumeration.OperationType; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.time.LocalDateTime; /** * 自定义切面实现公共字段自动填充处理逻辑 */ Aspect Component Slf4j public class AutoFillAspect { /** * 切入点 * */ Pointcut(execution(* com.sky.mapper.*.*(..)) annotation(com.sky.annotation.AutoFill)) public void autoFillPointcut(){} /** * 前置通知在通知中进行公共字段的赋值 * */ Before(autoFillPointcut()) public void autoFill(JoinPoint joinPoint) throws InvocationTargetException, IllegalAccessException { log.info(开始进行公共字段自动填充...); //获取当前被拦截的方法上的数据库操作类型 MethodSignature signature (MethodSignature) joinPoint.getSignature(); //方法签名对象 AutoFill autoFill signature.getMethod().getAnnotation(AutoFill.class); //方法上的注解对象 OperationType operationType autoFill.value(); //数据库操作类型 //获取当前被拦截的方法的参数→实体对象 Object[] args joinPoint.getArgs(); if(args null || args.length 0){ return; } Object entity args[0]; //准备赋值的数据 LocalDateTime now LocalDateTime.now(); long currentId BaseContext.getCurrentId(); //根据当前不同的操作类型 为对应的属性通过反射来赋值 if(operationType OperationType.INSERT){ //为四个公共字段赋值 try { Method setCreateTime entity.getClass().getMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class); Method setUpdateTime entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setCreateUser entity.getClass().getMethod(AutoFillConstant.SET_CREATE_USER, Long.class); Method setUpdateUser entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); //通过反射为对象属性赋值 setCreateTime.invoke(entity, now); setUpdateTime.invoke(entity, now); setCreateUser.invoke(entity, currentId); setUpdateUser.invoke(entity, currentId); } catch (NoSuchMethodException e) { e.printStackTrace(); } } else if(operationType OperationType.UPDATE){ //为两个公共字段赋值 try { Method setUpdateTime entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); Method setUpdateUser entity.getClass().getMethod(AutoFillConstant.SET_UPDATE_USER, Long.class); //通过反射为对象属性赋值 setUpdateTime.invoke(entity, now); setUpdateUser.invoke(entity, currentId); }catch (NoSuchMethodException e){ e.printStackTrace(); } } } }1.4 功能测试2. 新增菜品2.1 需求分析和设计业务规则(1)菜品名称必须是唯一的(2)菜品必须属于某个分类下 不能单独存在(3)新增菜品时可以根据情况选择菜品的口味(4)每个菜品必须对应一张图片接口设计根据类型查询分类已完成、文件上传、新增菜品数据库设计dish菜品表和dis_flavor口味表→一对多的关系2.2 代码开发2.2.1 开发文件上传接口浏览器→后端服务→阿里云OSS注后端接收的参数名必须与前端提交的参数名保持一致AliOssPropertiespackage com.sky.properties; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; Component //ConfigurationProperties为配置属性类读取配置文件的配置项 封装成以一个java对象 ConfigurationProperties(prefix sky.alioss) Data AllArgsConstructor NoArgsConstructor public class AliOssProperties { private String endpoint; private String accessKeyId; private String accessKeySecret; private String bucketName; }注配置属性类存在后 在xml文件写此配置相关数据会有提示在主配置文件application.xml看不到具体的配置信息 是通过profiles下的active选定环境 具体的配置信息在对应环境下进行配置为防止文件名重复 使用uuid来生成文件名注Swagger对于文件上传不友好 可通过postman或者前后端联调测试文件上传接口2.2.2 新增菜品因为新增菜品会操作菜品表和口味表两张表 所以为保证数据的一致性 需要在service实现方法上加Transactional注解 并在启动类上加EnableTransactionManagement来开启注解方式的事务管理前端无法传递dishId→菜品表id对应口味表dish_id 让菜品表id给口味表dish_id赋值DishMapper.xml注useGeneratedKeystrue→需要获取插入时所生成的主键值 keyPropertyid→把获取的主键值赋值给dish的id属性 这就表示insert语句执行完毕后 产生的主键值会赋给id属性此处修正DAY2篇的代码此处没有使用Result来封装 破坏了前后端约定的统一响应格式所以此处如果沿用DAY2篇代码在添加菜品页面的菜品分类中不显示数据正确代码已在DAY2篇进行修正添加口味时 在选择口味类型时要双击才能选择2.3 功能测试八、小tip后端接收的参数名必须与前端提交的参数名保持一致ConfigurationProperties为配置属性类读取配置文件的配置项 封装成以一个java对象因为新增菜品会操作菜品表和口味表两张表 所以为保证数据的一致性 需要在service实现方法上加Transactional注解 并在启动类上加EnableTransactionManagement来开启注解方式的事务管理前端无法传递dishId 在添加口味时dishId的值拿不到→菜品表id对应口味表dish_id 让菜品表id给口味表dish_id赋值前后端约定的统一响应格式一定要遵守