公共字段自动填充
问题分析
公共字段手动设置,代码冗余,不易于后期维护
实现思路![](https://img-blog.csdnimg.cn/direct/4ee93ee19b064d69aa7c59d8915aae71.jpeg)
代码开发
AutoFill注解
/**
* 自定义注解
* 标识方法需要进行功能字段自动填充处理
* @author 鼠鼠捏~~
* @version 1.0~~
*/
@Target(ElementType.METHOD)//指定该注解只能加在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
//数据库操作类型,UPDATE,INSERT
OperationType value();
}
AutoFillAspect切面类
/**
* 自定义切面,
* 实现公共字段自动填充
*
* @author 鼠鼠捏~~
* @version 1.0~~
*/
@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) {
log.info("开始进行公共字段自动填充...");
//1.获取当前被拦截方法的数据库操作类型
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象
AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
OperationType operationType = autoFill.value();//获得数据库操作类型
//2.获取当前被拦截方法的参数===>实体对象
Object[] args = joinPoint.getArgs();
//避免为空或null
if (args == null || args.length == 0) {
return;
}
//默认传参时,实体为第一个参数,取出该实体
Object entity = args[0];
//3.准备赋值数据 获取ID:BaseContext ==>ThreadLocal
LocalDateTime now = LocalDateTime.now();
Long currentId = BaseContext.getCurrentId();
//4.根据当前操作类型位不同属性进行赋值=====>反射操作
if (operationType == OperationType.INSERT) {
//为4个公共字段赋值
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) {
e.printStackTrace();
}
} 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) {
e.printStackTrace();
}
}
}
在Mapper层对象方法添加注解
@AutoFill(value = OperationType.INSERT)
@AutoFill(value = OperationType.UPDATE)
注释Service层对应代码
功能测试
新增菜品
问题分析
三个接口:
- 根据类型查询分类(已完成)
- 文件上传
- 新增菜品
实现思路
文件上传接口:
代码开发
文件上传接口:
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result<String> upload(MultipartFile file){
log.info("文件上传:{},",file);
try {
//原始文件名
String originalFilename = file.getOriginalFilename();
//截取后缀
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//UUID+后缀
String objectName = UUID.randomUUID().toString()+ extension;
//文件请求路径
String filePath = aliOssUtil.upload(file.getBytes(), objectName);
return Result.success(filePath);
} catch (IOException e) {
log.error("文件上传失败:{}",e);
}
return Result.error(MessageConstant.UPLOAD_FAILED);
}
/**
* 配置类,用于创建AliOssUtil对象
*
* @author 鼠鼠捏~~
* @version 1.0~~
*/
@Configuration
@Slf4j
public class OssConfiguration {
@Bean
@ConditionalOnMissingBean//没有bean时,才创建
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
//创建Util对象,将Properties属性赋值给Util对象
AliOssUtil aliOssUtil = new AliOssUtil(aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
return aliOssUtil;
}
新增菜品:
@Autowired
private DishService dishService;
/**
* 新增菜品
*
* @param dishDTO
* @return
*/
@PostMapping
@ApiOperation("新增菜品")
public Result save(@RequestBody DishDTO dishDTO) {
log.info("新增菜品:{}", dishDTO);
dishService.saveWithFlavour(dishDTO);
return Result.success();
@Autowired
private DishMapper dishMapper;
@Autowired
private DishFlavourMapper dishFlavourMapper;
/**
* 新增菜品及对应的口味
* @param dishDTO
*/
@Override
//添加菜品及口味,设计两张表,开始事务保证一致性
@Transactional
public void saveWithFlavour(DishDTO dishDTO) {
//DTO对象包含口味,创建dish对象,属性拷贝
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
//向菜品表插入数据,一次一条
dishMapper.insert(dish);
//获取insert语句生成的主键值
Long dishId = dish.getId();
//向口味表插入数据,可能多条
List<DishFlavor> flavors = dishDTO.getFlavors();
if (flavors != null && flavors.size() >0){
//遍历,设置dishId
flavors.forEach(dishFlavor -> {
dishFlavor.setDishId(dishId);
});
//批量插入
dishFlavourMapper.insertBatch(flavors);
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.DishMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">-- 获得insert语句生成的主键值,并赋值给id
insert into dish(name, category_id, price, image, description, create_time, update_time, create_user,
update_user, status)
value
(#{name},#{categoryId},#{price},#{image},#{description},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.DishMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">-- 获得insert语句生成的主键值,并赋值给id
insert into dish(name, category_id, price, image, description, create_time, update_time, create_user,
update_user, status)
value
(#{name},#{categoryId},#{price},#{image},#{description},#{createTime},#{updateTime},#{createUser},#{updateUser},#{status})
</insert>
</mapper>
功能测试
菜品分页查询
问题分析
根据页码展示菜品信息,每页10条数据,可根据菜品名称,菜品分类,菜品状态进行查询
实现思路
代码开发
@GetMapping("/page")
@ApiOperation("菜品分页查询")
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO){
log.info("菜品分页查询:{}",dishPageQueryDTO);
PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
return Result.success(pageResult);
}
@Override
public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {
PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());
Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
//封装并返回 总记录数及数据
return new PageResult(page.getTotal(),page.getResult());
}
<select id="pageQuery" resultType="com.sky.vo.DishVO">
select d.*,c.name as categoryName
from dish d left outer join category c on d.category_id = c.id
<where>
<if test="name !=null">
and d.name like concat('%',#{name},'%')
</if>
<if test="categoryId !=null">
and d.category_id = #{categoryId}
</if>
<if test="status !=null">
and d.status = #{status}
</if>
</where>
order by d.create_time desc
</select>
功能测试
删除菜品
问题分析
- 可以一次删除一个或多个菜品
- 起售中的菜品不能删
- 被套餐关联的菜品不能删除
- 删除菜品后,关联的口味数据也需要删除
实现思路
代码开发
/**
* 批量删除菜品
* @param ids
* @return
*/
@Override
public void deleteBatch(List<Long> ids) {
//判断是否起售===>是否能够删除
for (Long id : ids) {
Dish dish = dishMapper.geById(id);
if(dish.getStatus() == StatusConstant.ENABLE){
//起售中,不能删除
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
}
//判断是否被套餐关联===>是否能够删除
List<Long> setmealIds = setmealDishMapper.getSetmealIdByDishIds(ids);
if (setmealIds != null && setmealIds.size() >0){
//被套餐关联,不能删除
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
//删除菜品数据
// for (Long id : ids) {
// dishMapper.deleteById(id);
// //删除该菜品关联的口味数据
// //不查询直接删除
// dishFlavourMapper.deleteByDishId(id);
// //sql : delete from dish where id in (?,?,?)
// }
//根据菜品id集合批量删除菜品数据
dishMapper.deleteByIds(ids);
//sql : delete from dish where id in (?,?,?)
//根据菜品id集合批量删除菜品关联的口味数据
dishFlavourMapper.deleteByDishIds(ids);
//sql : delete from dish_flavour where dish_id in (?,?,?)
}
<delete id="deleteByDishIds">
delete
from dish_flavor
where dish_id in
<foreach collection="Dishids" item="Dishid" separator="," open="(" close=")">
#{Dishid}
</foreach>
</delete>
<delete id="deleteByIds">
delete from dish where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
功能测试
修改菜品
问题分析
接口设计:
根据id查询菜品及关联的口味
根据类型查询分类(已实现)
文件上传(已实现)
修改菜品
实现思路
查询时,先查菜品表,再查口味表,然后封装在一起
修改菜品时,包含两部分,修改菜品及对应口味数据,其中,修改口味数据时,为方便操作,统一先删除原数据,再插入新数据
代码开发
/**
* 根据id查询菜品及对应口味数据
* @param id
* @return
*/
@Override
public DishVO getByIdWithFlavour(Long id) {
//先查菜品表
Dish dish = dishMapper.getById(id);
//再查口味表
List<DishFlavor> dishFlavors = dishFlavourMapper.getByDishId(id);
//封装
DishVO dishVO = new DishVO();
BeanUtils.copyProperties(dish,dishVO);
dishVO.setFlavors(dishFlavors);
return dishVO;
}
/**
* 根据id修改菜品及对应口味数据
* @param dishDTO
*/
@Override
@Transactional
public void updateWithFlavour(DishDTO dishDTO) {
Dish dish = new Dish();
BeanUtils.copyProperties(dishDTO,dish);
//修改菜品表数据
dishMapper.update(dish);
//修改口味数据时====>先统一删除原数据,再插入新数据
//1.删除口味数据
dishFlavourMapper.deleteByDishId(dishDTO.getId());
//2.插入新口味数据
List<DishFlavor> flavors = dishDTO.getFlavors();
dishFlavourMapper.insertBatch(flavors);
if (flavors != null && flavors.size() >0){
//遍历,设置dishId
flavors.forEach(dishFlavor -> {
dishFlavor.setDishId(dishDTO.getId());
});
//批量插入
dishFlavourMapper.insertBatch(flavors);
}
}