目录
一、公共字段自动填充
1.出现的问题
在上一章节我们已经完成了后台系统的员工管理功能和菜品分类功能的开发,在新增员工或者新增菜品分类时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工或者编辑菜品分类时需要设置修改时间、修改人等字段。这些字段属于公共字段,也就是也就是在我们的系统中很多表中都会有这些字段,如下:
新增员工方法:
编辑员工方法:
我们使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。
2. 实现思路
在实现公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。在上述的问题分析中,我们提到有四个公共字段,需要在新增/更新中进行赋值操作, 具体情况如下:
实现步骤:
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
3). 在 Mapper 的方法上加入 AutoFill 注解
若要实现上述步骤,需掌握以下知识(之前课程内容都学过)
技术点:枚举、注解、AOP、反射
3. 代码开发
//**自定义注解 AutoFill**
//进入到sky-server模块,创建com.sky.annotation包
package 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();
}
-------------------------
//其中OperationType已在sky-common模块中定义package com.sky.enumeration;
/**
* 数据库操作类型,枚举
*/
public enum OperationType {
/**
* 更新操作
*/
UPDATE,
/**
* 插入操作
*/
INSERT
}
-------------------------
// Mapper里方法加注解
//在新增和修改的mapper上加
@AutoFill(OperationType.INSERT)
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("INSERT INTO dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user) " +
"VALUES (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
void insert(Dish dish);
@AutoFill(OperationType.UPDATE)
void updateById(Dish dish);
------------------------
//### 自定义切面AutoFillAspect
//在sky-server模块,创建com.sky.aspect包。
package 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.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
/**
* @author liuyp
* @since 2023/09/01
*/
@Slf4j
@Aspect
@Component
public class AutofillAspect {
@Before("execution(* com.sky.mapper.*.*(..)) && @annotation(autoFill)")
public void autoFill(JoinPoint joinPoint, AutoFill autoFill){
log.info("开始自动填充公共字段值");
//获取Mapper方法的参数:entity对象
Object[] args = joinPoint.getArgs();
if (args == null || args.length == 0) {
return;
}
Object entity = args[0];
//准备要赋的值
LocalDateTime now = LocalDateTime.now();
Long currentUser = BaseContext.getCurrentId();
//判断操作的类型
Class<?> clazz = entity.getClass();
if (autoFill.value() == OperationType.INSERT) {
try {
//是新增操作:获取setCreateTime、setUpdateTime、setCreateUser、setUpdateUser四个方法
Method setCreateTimeMethod = clazz.getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
Method setUpdateTimeMethod = clazz.getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setCreatorMethod = clazz.getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);
Method setUpdatorMethod = clazz.getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//使用反射调用entity的对应方法,给属性赋值
setCreateTimeMethod.invoke(entity, now);
setUpdateTimeMethod.invoke(entity, now);
setCreatorMethod.invoke(entity, currentUser);
setUpdatorMethod.invoke(entity, currentUser);
} catch (Exception e) {
e.printStackTrace();
}
}else{
try {
//是修改操作:获取setUpdateTime、setUpdateUser两个方法
Method setUpdateTimeMethod = clazz.getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
Method setUpdatorMethod = clazz.getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
//使用反射调用entity的对应方法,给属性赋值
setUpdateTimeMethod.invoke(entity, now);
setUpdatorMethod.invoke(entity, currentUser);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
二、新增菜品
1 需求分析与设计
1 产品原型
2 接口设计
根据上述原型图先粗粒度设计接口,共包含3个接口。
接口设计:
-
根据类型查询分类(已完成)
-
文件上传
-
新增菜品
接下来细粒度分析每个接口,明确每个接口的请求方式、请求路径、传入参数和返回值。
1. 根据类型查询分类
2. 文件上传
3. 新增菜品
3 表设计
2 代码开发
1 文件上传实现
//(1) 配置OSS参数
//在sky-server模块application-dev.yml
sky:
alioss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKeyId: LTAI5tG3TbA9HLs22KEtimyB
accessKeySecret: 4avUxhaO5KCTl5pqpta3AdU98mT9um
bucketName: itheima-liuyp
-----------------------------
// 2) 读取配置参数(已完成)
//在sky-common模块中,已定义
package com.sky.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
------------------------
// 3) 将OSS工具对象放到容器里
//在sky-server模块
package com.sky.config;
import com.sky.properties.AliOssProperties;
import com.sky.utils.AliOssUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author liuyp
* @since 2023/09/01
*/
@Configuration
public class OssConfig {
@Bean
@ConditionalOnMissingBean
public AliOssUtil ossUtil(AliOssProperties properties){
return new AliOssUtil(
properties.getEndpoint(),
properties.getAccessKeyId(),
properties.getAccessKeySecret(),
properties.getBucketName()
);
}
}
------------------------
//其中,AliOssUtil.java已在sky-common模块中定义
package com.sky.utils;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayInputStream;
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
/**
* 文件上传
*
* @param bytes
* @param objectName
* @return
*/
public String upload(byte[] bytes, String objectName) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建PutObject请求。
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
//文件访问路径规则 https://BucketName.Endpoint/ObjectName
StringBuilder stringBuilder = new StringBuilder("https://");
stringBuilder
.append(bucketName)
.append(".")
.append(endpoint)
.append("/")
.append(objectName);
log.info("文件上传到:{}", stringBuilder.toString());
return stringBuilder.toString();
}
}
-------------------------
//4) CommonController
package com.sky.controller.admin;
import com.sky.constant.MessageConstant;
import com.sky.result.Result;
import com.sky.utils.AliOssUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
/**
* 通用接口
*/
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {
@Autowired
private AliOssUtil aliOssUtil;
/**
* 文件上传
* @param file
* @return
*/
@PostMapping("/upload")
@ApiOperation("文件上传")
public Result<String> upload(MultipartFile file){
log.info("文件上传:{}",file);
try {
//原始文件名
String originalFilename = file.getOriginalFilename();
//截取原始文件名的后缀 dfdfdf.png
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//构造新文件名称
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);
}
}
2 新增菜品实现
//1)准备DTO类(已完成)
//在sky-pojo模块中
package com.sky.dto;
import com.sky.entity.DishFlavor;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Data
public class DishDTO implements Serializable {
private Long id;
//菜品名称
private String name;
//菜品分类id
private Long categoryId;
//菜品价格
private BigDecimal price;
//图片
private String image;
//描述信息
private String description;
//0 停售 1 起售
private Integer status;
//口味
private List<DishFlavor> flavors = new ArrayList<>();
}
-----------------------------
// 2) DishController
进入到sky-server模块
package com.sky.controller.admin;
import com.sky.dto.DishDTO;
import com.sky.result.Result;
import com.sky.service.DishService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品管理相关接口")
public class DishController {
@Autowired
private DishService dishService;
@PostMapping
@ApiOperation("新增菜品")
public Result addDish(@RequestBody DishDTO dto){
return dishService.addDish(dto);
}
}
------------------------
//3) DishService
package com.sky.service;
import com.sky.dto.DishDTO;
import com.sky.result.Result;
public interface DishService {
/**
* 新增菜品
* @param dto
* @return
*/
Result addDish(DishDTO dto);
}
--------------------------
//4) DishServiceImpl
package com.sky.service.impl;
import com.sky.dto.DishDTO;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.mapper.DishFlavorMapper;
import com.sky.mapper.DishMapper;
import com.sky.result.Result;
import com.sky.service.DishService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.util.List;
@Service
public class DishServiceImpl implements DishService {
@Autowired
private DishMapper dishMapper;
@Autowired
private DishFlavorMapper dishFlavorMapper;
@Override
@Transactional
public Result addDish(DishDTO dto) {
//保存菜品
Dish dish = new Dish();
BeanUtils.copyProperties(dto, dish);
dishMapper.insert(dish);
//保存菜品与品味的关系
List<DishFlavor> flavors = dto.getFlavors();
if (!CollectionUtils.isEmpty(flavors)) {
flavors.forEach(dishFlavor -> dishFlavor.setDishId(dish.getId()));
dishFlavorMapper.batchInsert(flavors);
}
return Result.success();
}
}
---------------------
// 5) DishMapper
DishMapper.java中添加
@AutoFill(OperationType.INSERT)
@Options( = true, keyProperty = "id")
@Insert("INSERT INTO dish (name, category_id, price, image, description, status, create_time, update_time, create_user, update_user) " +
"VALUES (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")
void insert(Dish dish);
----------------------
//6) DishFlavorMapper
package com.sky.mapper;
import com.sky.entity.DishFlavor;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface DishFlavorMapper {
void batchInsert(List<DishFlavor> flavors);
}
-----------------------
//7) DishFlavorMapper.xml
//在/resources/mapper中创建DishFlavorMapper.xml
<?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.DishFlavorMapper">
<insert id="batchInsert">
INSERT INTO dish_flavor (dish_id, name, value) VALUES
<foreach collection="flavors" item="f" separator=",">
(#{f.dishId}, #{f.name}, #{f.value})
</foreach>
</insert>
</mapper>
三、菜品分页查询
1 需求分析和设计
1 产品原型
业务规则:
-
根据页码展示菜品信息
-
每页展示10条数据
-
分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询
2 接口设计
2 代码开发
// 1 设计DTO类
//根据菜品分页查询接口定义设计对应的DTO:**
//在sky-pojo模块中,已定义
package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class DishPageQueryDTO implements Serializable {
private int page;
private int pageSize;
private String name;
//分类id
private Integer categoryId;
//状态 0表示禁用 1表示启用
private Integer status;
}
-------------------
//2 设计VO类
//根据菜品分页查询接口定义设计对应的VO:
//在sky-pojo模块中,已定义
package com.sky.vo;
import com.sky.entity.DishFlavor;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DishVO implements Serializable {
private Long id;
//菜品名称
private String name;
//菜品分类id
private Long categoryId;
//菜品价格
private BigDecimal price;
//图片
private String image;
//描述信息
private String description;
//0 停售 1 起售
private Integer status;
//更新时间
private LocalDateTime updateTime;
//分类名称
private String categoryName;
//菜品关联的口味
private List<DishFlavor> flavors = new ArrayList<>();
}
---------------------
//3 DishController
//根据接口定义创建DishController的page分页查询方法:
@GetMapping("/page")
@ApiOperation("分页查询菜品")
public Result queryDishesByPage(DishPageQueryDTO dto){
return dishService.queryDishesByPage(dto);
}
--------------------
// 4 DishService
//在 DishService 中扩展分页查询方法:
/**
* 分页查询菜品
* @param dto
* @return
*/
Result queryDishesByPage(DishPageQueryDTO dto);
-----------------
//5 DishServiceImpl
//在 DishServiceImpl 中实现分页查询方法
@Override
public Result queryDishesByPage(DishPageQueryDTO dto) {
//开启分页
PageHelper.startPage(dto.getPage(), dto.getPageSize());
//查询列表
Page<DishVO> page = dishMapper.selectByPage(dto);
//封装结果
PageResult result = new PageResult(page
.getTotal(), page.getResult());
return Result.success(result);
}
------------------
//6 DishMapper
//在 DishMapper 接口中声明 pageQuery 方法:
Page<DishVO> selectByPage(DishPageQueryDTO dto);
-------------------
//7 DishMapper.xml
//在 resources/mapper/DishMapper.xml 中编写SQL
<?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">
<select id="selectByPage" resultType="com.sky.vo.DishVO">
select d.*, c.name as categoryName from dish d left join category c on d.category_id = c.id
<where>
<if test="name!=null and name.length()>0">
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 create_time desc
</select>
</mapper>
四、删除菜品
1 需求分析和设计
1 产品原型
业务规则:
-
可以一次删除一个菜品,也可以批量删除菜品
-
起售中的菜品不能删除
-
被套餐关联的菜品不能删除
-
删除菜品后,关联的口味数据也需要删除掉
2 接口设计
3 注意事项
-
如果有菜品状态是“起售”状态,也不允许删除
-
若删除的菜品数据关联着某个套餐,此时,删除失败(setmeal_dish表为菜品和套餐关联的中间表。)
-
在dish表中删除菜品基本数据时,同时,也要把关联在dish_flavor表中的数据一块删除。
2 代码开发
//DishController
@DeleteMapping
@ApiOperation("删除菜品")
public Result deleteDishesByIds(@RequestParam("ids") List<Long> ids){
return dishService.deleteDishesByIds(ids);
}
-----------------
//DishService
/**
* 删除菜品
* @param ids
* @return
*/
Result deleteDishesByIds(List<Long> ids);
---------------
//DishServiceImpl
@Autowired
private DishMapper dishMapper;
@Autowired
private DishFlavorMapper dishFlavorMapper;
@Autowired
private SetmealDishMapper setmealDishMapper;
@Override
@Transactional
public Result deleteDishesByIds(List<Long> ids) {
//1. 如果ids对应的菜品里,有未停售的菜品,则不能删除
int enableDishCount = dishMapper.selectEnableDishCountByIds(ids);
if (enableDishCount > 0) {
throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
}
//2. 如果ids对应的菜品里,有关联的套餐,则简单处理,直接不予删除
int setmealCount = setmealDishMapper.selectSetmealCountByDishIds(ids);
if (setmealCount > 0) {
throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
}
//删除菜品
dishMapper.batchDeleteByIds(ids);
//删除菜品对应的口味
dishFlavorMapper.batchDeleteByDishIds(ids);
return Result.success();
}
-----------------
//DishMapper
//在DishMapper中声明getById方法,并配置SQL
/**
* 查询ids对应的菜品中,启售中状态的菜品数量
*/
int selectEnableDishCountByIds(List<Long> ids);
/**
* 根据ids集合,批量删除对应的菜品
*/
void batchDeleteByIds(List<Long> ids);
-----------------
//DishMapper.xml
<select id="selectEnableDishCountByIds" resultType="int">
select count(*) from dish
<where>
<foreach collection="ids" item="id" separator="," open="and id in (" close=")">
#{id}
</foreach>
and status = 1
</where>
</select>
<delete id="batchDeleteByIds">
delete from dish where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
-----------------
//SetmealDishMapper
package com.sky.mapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface SetmealDishMapper {
/**
* 根据菜品id集合,查询关联的套餐数量
*/
int selectSetmealCountByDishIds(List<Long> ids);
}
-----------------
//SetmealDishMapper.xml
<?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.SetmealDishMapper">
<select id="selectSetmealCountByDishIds" resultType="int">
select count(*) from setmeal_dish where id in
<foreach collection="ids" item="dishId" separator="," open="(" close=")">
#{dishId}
</foreach>
</select>
</mapper>
五、修改菜品
1 需求分析和设计
1 产品原型
2 接口设计
1). 根据id查询菜品
2). 修改菜品
2 代码开发
修改功能先要根据id返回数据,然后才能改数据
1 根据id查询菜品实现
//1 根据id查询菜品实现
//1) DishController
@GetMapping("/{id}")
@ApiOperation("根据id查询菜品")
public Result queryDishById(@PathVariable("id") Long id) {
return dishService.queryDishById(id);
}
-----------------
2) DishService
/**
* 根据id查询菜品
* @param id
* @return
*/
Result queryDishById(Long id);
-----------------
3) DishServiceImpl
@Override
public Result queryDishById(Long id) {
//根据id查询菜品信息
Dish dish = dishMapper.selectById(id);
//根据菜品id查询口味列表
List<DishFlavor> dishFlavors = dishFlavorMapper.selectByDishId(id);
//封装结果
DishVO vo = new DishVO();
BeanUtils.copyProperties(dish, vo);
vo.setFlavors(dishFlavors);
return Result.success(vo);
}
-----------------
4) DishMapper
@Select("select * from dish where id=#{id}")
Dish selectById(Long id);
-----------------
5) DishFlavorMapper
/**
* 根据菜品id,查询品味列表
*/
@Select("select * from dish_flavor where dish_id = #{dishId}")
List<DishFlavor> selectByDishId(Long dishId);
2 修改菜品实现
//1) DishController
//根据修改菜品的接口定义在DishController中创建方法:
@PutMapping
@ApiOperation("修改菜品")
public Result updateDishById(@RequestBody DishDTO dto){
return dishService.updateDishById(dto);
}
--------------
//2) DishService
//在DishService接口中声明updateWithFlavor方法:
/**
* 根据id修改菜品
* @param dto
* @return
*/
Result updateDishById(DishDTO dto);
--------------
//3) DishServiceImpl
//在DishServiceImpl中实现updateWithFlavor方法:
@Override
public Result updateDishById(DishDTO dto) {
//1. 修改菜品信息
Dish dish = new Dish();
BeanUtils.copyProperties(dto, dish);
dishMapper.updateById(dish);
//2. 删除菜品对应的原口味列表
dishFlavorMapper.batchDeleteByDishIds(Collections.singletonList(dto.getId()));
//3. 重新添加菜品的口味列表
List<DishFlavor> flavors = dto.getFlavors();
if (!CollectionUtils.isEmpty(flavors)) {
flavors.forEach(dishFlavor -> dishFlavor.setDishId(dto.getId()));
dishFlavorMapper.batchInsert(flavors);
}
return Result.success();
}
--------------
//4) DishMapper
@AutoFill(OperationType.UPDATE)
void updateById(Dish dish);
--------------
//5) DishMapper.xml
<update id="updateById">
UPDATE dish
<set>
<if test="name!=null and name.length()>0">name = #{name},</if>
<if test="categoryId!=null">category_id = #{categoryId},</if>
<if test="price!=null">price = #{price},</if>
<if test="image!=null and image.length()>0">image = #{image},</if>
<if test="description!=null and description.length()>0">description = #{description},</if>
<if test="status!=null">status = #{status},</if>
<if test="updateTime!=null">update_time = #{updateTime},</if>
<if test="updateUser!=null">update_user = #{updateUser}</if>
</set>
WHERE id = #{id}
</update>