苍穹外卖阶段性总结 (超详细版)
目录
一 管理端员工的相关接口
接口1 实现员工登录
接口2 实现员工退出
接口3 实现新增员工
接口4 员工分页查询
接口5 启用禁用员工账号
接口6 根据id查询员工
接口7 编辑员工信息
二 菜品相关接口
接口1 新增菜品
接口2 修改菜品
接口3 删除菜品
接口4 根据分类的id查询菜品
接口5 菜品分页查询
接口6 根据id查询菜品和口味信息
三 套餐管理相关接口
接口1 新增套餐
接口2 修改套餐
接口3 套餐分页查询
接口4 套餐批量删除
接口5 根据id查询套餐(回显)
接口6 套餐的启售与停售
一 管理端员工的相关接口
三层架构模式(Controller控制层、Service业务层、Mapper持久层)
1 控制层EmployeeController
package com.sky.controller.admin;import com.sky.constant.JwtClaimsConstant;
import com.sky.dto.EmployeeDTO;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.entity.Employee;
import com.sky.properties.JwtProperties;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.EmployeeService;
import com.sky.utils.JwtUtil;
import com.sky.vo.EmployeeLoginVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;/*** 员工管理*/
@RestController
@Tag(name = "员工相关接口")
@RequestMapping("/admin/employee")
@Slf4j
public class EmployeeController {@Autowiredprivate EmployeeService employeeService;@Autowiredprivate JwtProperties jwtProperties;/*** 登录** @param employeeLoginDTO* @return*/@PostMapping("/login")@Operation(summary = "员工登录")@Tag(name = "员工相关接口")public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {log.info("员工登录:{}", employeeLoginDTO);Employee employee = employeeService.login(employeeLoginDTO);//登录成功后,生成jwt令牌Map<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.EMP_ID, employee.getId());String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),jwtProperties.getAdminTtl(),claims);EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder().id(employee.getId()).userName(employee.getUsername()).name(employee.getName()).token(token).build();return Result.success(employeeLoginVO);}/*** 退出** @return*/@Operation(summary = "员工退出")@PostMapping("/logout")@Tag(name = "员工相关接口")public Result<String> logout() {return Result.success();}/*** 新增员工** @param employeeDto* @return*/@Operation(summary = "新增员工")@Tag(name = "员工相关接口")@PostMappingpublic Result save(@RequestBody EmployeeDTO employeeDto) {//Json格式的参数,用@RequestBody接收log.info("新增员工:{}", employeeDto);employeeService.save(employeeDto);return Result.success();}
// 问No mapping for POST /admin/employee怎么解决的,看下你的save方法上面的注解@PostMapping后面有没有("/save"),有的话要删了/*** 员工分页查询** @param employeePageQueryDTO* @return*/@Operation(summary = "员工分页查询")@Tag(name = "员工相关接口")@GetMapping("/page")public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {log.info("员工分页查询:{}", employeePageQueryDTO);PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);return Result.success(pageResult);}/*** 启用禁用员工账号** @param status* @param id* @return*/@Tag(name = "员工相关接口")@Operation(summary = "启用禁用员工账号")@PostMapping("/status/{status}")public Result startOrStop(@PathVariable("status") Integer status, Long id) {log.info("启用禁用员工账号:{},{}", status, id);employeeService.startOrStop(status, id);return Result.success();}/*** 根据id查询员工** @param id* @return*/@Operation(summary = "根据id查询员工")@Tag(name = "员工相关接口")@GetMapping("/{id}")public Result<Employee> getById(@PathVariable Long id) {log.info("根据id查询员工:{}", id);Employee employee = employeeService.getById(id);return Result.success(employee);}/*** 编辑员工信息** @param employeeDTO* @return*/@Tag(name = "员工相关接口")@Operation(summary = "编辑员工信息")@PutMappingpublic Result update(@RequestBody EmployeeDTO employeeDTO) {log.info("编辑员工信息:{}", employeeDTO);employeeService.update(employeeDTO);return Result.success();}
}
职责:处理HTTP请求,协调请求与响应
分析:
接口1 实现员工登录
/*** 登录** @param employeeLoginDTO* @return*/@PostMapping("/login")@Operation(summary = "员工登录")@Tag(name = "员工相关接口")public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {log.info("员工登录:{}", employeeLoginDTO);Employee employee = employeeService.login(employeeLoginDTO);//登录成功后,生成jwt令牌Map<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.EMP_ID, employee.getId());String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),jwtProperties.getAdminTtl(),claims);EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder().id(employee.getId()).userName(employee.getUsername()).name(employee.getName()).token(token).build();return Result.success(employeeLoginVO);}
1 @RequestBody EmployeeLoginDTO employeeLoginDTO 作用是借助@RequestBoby的作用将客户端发送的HTTP 请求体中的数据绑定到控制器方法的参数上,使其自动封装给指定的对象EmployeeLogin
package com.sky.dto;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.io.Serializable;@Data
@ApiModel(description = "员工登录时传递的数据模型")
public class EmployeeLoginDTO implements Serializable {@ApiModelProperty("用户名")private String username;@ApiModelProperty("密码")private String password;}
2 执行员工登录功能
Employee employee = employeeService.login(employeeLoginDTO);
进入service服务层
/*** 员工登录** @param employeeLoginDTO*/Employee login(EmployeeLoginDTO employeeLoginDTO);
实现类
/*** 员工登录** @param employeeLoginDTO* @return*/public Employee login(EmployeeLoginDTO employeeLoginDTO) {String username = employeeLoginDTO.getUsername();String password = employeeLoginDTO.getPassword();//1、根据用户名查询数据库中的数据Employee employee = employeeMapper.getByUsername(username);//2、处理各种异常情况(用户名不存在、密码不对、账号被锁定)if (employee == null) {//账号不存在throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);}//密码比对// TODO 后期需要进行md5加密,然后再进行比对password = DigestUtils.md5DigestAsHex(password.getBytes());if (!password.equals(employee.getPassword())) {//密码错误throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);}if (employee.getStatus() == StatusConstant.DISABLE) {//账号被锁定throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);}//3、返回实体对象return employee;}
这里也需要Mapper层对数据库的访问处理
@Autowiredprivate EmployeeMapper employeeMapper;
先将DTO中传输的数据获取出来,用于后续的判断
String username = employeeLoginDTO.getUsername();String password = employeeLoginDTO.getPassword();
调用Mapper层的方法对用户名进行查询
Employee employee = employeeMapper.getByUsername(username);
Mapper层(这里的语句较为简单直接使用注解,就没有使用xml进行查询了)
/*** 根据用户名查询员工* @param username* @return*/@Select("select * from employee where username = #{username}")Employee getByUsername(String username);
获取之后进行判断(账号对象是否为空)
if (employee == null) {//账号不存在throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);}
(密码是否正确,错误的话抛出异常)
password = DigestUtils.md5DigestAsHex(password.getBytes());if (!password.equals(employee.getPassword())) {//密码错误throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);}
(账号状态是否被锁定,锁定的话抛出异常)
if (employee.getStatus() == StatusConstant.DISABLE) {//账号被锁定throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);}
(如果前面都正常通过校验则登录成功返回实体对象)
return employee;
3 员工登陆成功,生成jwt令牌
生成jwt令牌的作用主要是便于后续进行操作的便携性,解决了登陆状态的持续验证问题。这里的实现是后端借助前端登录的信息,生成token,后续用户再访问客户端的其他功能,服务端只需要验证其token即可。验证过程:使用相同的密钥对Token进行签名验证,同时还会验证其是否过期
Map<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.EMP_ID, employee.getId());String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(),jwtProperties.getAdminTtl(),claims);
大致生成的token(键值对的先后顺序不影响)
{"emp_id": 12345,"exp": 1698765432, // 过期时间(由 jwtProperties.getAdminTtl() 决定)"iat": 1698761832 // 签发时间(通常由库自动生成)
}
token是借助密钥生成的,密钥也用于验证token。后端只存储密钥,token由前端存储。(这里是存储在配置文件当中的密钥)
jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key: itcast# 设置jwt过期时间admin-ttl: 7200000# 设置前端传递过来的令牌名称admin-token-name: token
将后端对象转换为前端需要的响应格式,同时利用构建器模式提升代码的可维护性和可读性。
EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder().id(employee.getId()).userName(employee.getUsername()).name(employee.getName()).token(token).build();
将登录成功的员工信息(employeeLoginVO
)封装到 Result
对象中,响应给前端
return Result.success(employeeLoginVO);
大致的响应样式
{"code": 200,"message": "成功","data": {"id": 123,"userName": "john_doe","name": "John Doe","token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
}
接口2 实现员工退出
/*** 退出** @return*/@Operation(summary = "员工退出")@PostMapping("/logout")@Tag(name = "员工相关接口")public Result<String> logout() {return Result.success();}
接口3 实现新增员工
/*** 新增员工** @param employeeDto* @return*/@Operation(summary = "新增员工")@Tag(name = "员工相关接口")@PostMappingpublic Result save(@RequestBody EmployeeDTO employeeDto) {//Json格式的参数,用@RequestBody接收log.info("新增员工:{}", employeeDto);employeeService.save(employeeDto);return Result.success();}
分析:
调用service接口的方法
/*** 新增员工** @param employeeDto*/void save(EmployeeDTO employeeDto);
实现类
/*** 新增员工** @param employeeDto*/@Overridepublic void save(EmployeeDTO employeeDto) {Employee employee = new Employee();//使用BeanUtils.copyProperties方法将employeeDto中的属性值复制到employee对象中BeanUtils.copyProperties(employeeDto, employee);//设置状态employee.setStatus(StatusConstant.ENABLE);//设置密码,使用默认密码(md5加密)employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));employeeMapper.insert(employee);}
接收前端传递过来的员工信息
这里的DTO是前端传递过来的employee对象,里面的很多属性没有实现,我们需要对其先进行拷贝,再设置其状态(默认启用),再对其密码进行md5加密,调用Mapper层的insert将其存储再数据库的用户表当中。同时这里还使用了切面编程,将四个时间添加上。
/*** 插入员工数据* @param employee*/@Insert("insert into employee (name, username, password, phone, sex, id_number, status, create_time, update_time, create_user, update_user) " +"values (#{name}, #{username}, #{password}, #{phone}, #{sex}, #{idNumber}, #{status}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser})")@AutoFill(OperationType.INSERT)void insert(Employee employee);
接口4 员工分页查询
/*** 员工分页查询** @param employeePageQueryDTO* @return*/@Operation(summary = "员工分页查询")@Tag(name = "员工相关接口")@GetMapping("/page")public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO) {log.info("员工分页查询:{}", employeePageQueryDTO);PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);return Result.success(pageResult);}
employeeDTO
@Data
public class EmployeePageQueryDTO implements Serializable {//员工姓名private String name;//页码private int page;//每页显示记录数private int pageSize;}
调用Service业务层的接口
PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);
分页查询service接口
/*** 分页查询** @param employeePageQueryDTO* @return*/PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
实现类
/*** 员工分页查询* @param employeePageQueryDTO* @return*/@Overridepublic PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {//设置分页参数PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());//查询数据Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);//封装结果并返回long total = page.getTotal();List<Employee> result = page.getResult();return new PageResult(total, result);}
作用:
使用 MyBatis 分页插件 PageHelper 启动分页,自动拦截后续的 SQL 查询并添加分页逻辑(LIMIT
子句)。
参数:
employeePageQueryDTO.getPage()
:当前页码(从 第1页 开始)。
employeePageQueryDTO.getPageSize()
:每页记录数(如 10、20)。
底层原理:
PageHelper 通过 ThreadLocal 保存分页参数,后续的 第一个 SQL 查询会被自动分页。
设置分页参数页码与尺寸大小,调用Mapper接口的查询方法
/*** 分页查询* @param employeePageQueryDTO*/Page<Employee> pageQuery(EmployeePageQueryDTO employeePageQueryDTO);
MapperXML文件
<!-- 信息的查询 --><select id="pageQuery" resultType="com.sky.entity.Employee">select * from employee<where><if test="name != null and name != ''">and name like concat('%',#{name},'%')</if></where>order by create_time desc</select>
查询结束后返回PageResult对象,参数是数据条数与数据组成的集合
接口5 启用禁用员工账号
/*** 启用禁用员工账号** @param status* @param id* @return*/@Tag(name = "员工相关接口")@Operation(summary = "启用禁用员工账号")@PostMapping("/status/{status}")public Result startOrStop(@PathVariable("status") Integer status, Long id) {log.info("启用禁用员工账号:{},{}", status, id);employeeService.startOrStop(status, id);return Result.success();}
1 PathVariable注释的作用是从url路径当中获取参数
@PathVariable("status") Integer status, Long id
2 调用服务层的方法
employeeService.startOrStop(status, id);
service接口
/*** 启用禁用员工账号** @param status* @param id*/void startOrStop(Integer status, Long id);
接口的实现类构建一个对象将传递的状态值赋值给对象,调用Mapper层的数据库操作方法
/*** 启用禁用员工账号* @param status* @param id*/@Overridepublic void startOrStop(Integer status, Long id) {//使用构建器(创建对象设置属性)Employee employee = Employee.builder().status(status).id(id).build();employeeMapper.update(employee);}
Mapper接口(这里的底层就是对数据库信息的更新,同时也使用了切面编程处理时间这些公共字段的更新操作
注解标记方法 + 切面拦截)
/*** 修改员工信息* @param employee*/@AutoFill(OperationType.UPDATE)void update(Employee employee);
MapperXML文件
<!-- 信息的更新 --><update id="update">update employee<set><if test="name != null">name = #{name},</if><if test="phone != null">phone = #{phone},</if><if test="sex != null">sex = #{sex},</if><if test="idNumber != null">id_number = #{idNumber},</if><if test="status != null">status = #{status},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{update}</if></set>where id = #{id}</update>
接口6 根据id查询员工
/*** 根据id查询员工** @param id* @return*/@Operation(summary = "根据id查询员工")@Tag(name = "员工相关接口")@GetMapping("/{id}")public Result<Employee> getById(@PathVariable Long id) {log.info("根据id查询员工:{}", id);Employee employee = employeeService.getById(id);return Result.success(employee);}
1 @PathVariable从url请求当中获取参数id
public Result<Employee> getById(@PathVariable Long id)
2 调用service的查询方法返回值为Employee对象
/*** 根据id查询员工** @param id* @return*/Employee getById(Long id);
3 接口的实现类(出于对安全性的考虑,这里要对密码进行隐藏处理)
/*** 根据id查询员工* * @param id* @return*/@Overridepublic Employee getById(Long id) {Employee employee = employeeMapper.getById(id);employee.setPassword("****");return employee;}
4 对应的Mapper接口类
/*** 根据id查询员工* @param id* @return*/Employee getById(Long id);
5 MapperXML文件
<!-- 根据id查询员工 --><select id="getById" resultType="com.sky.entity.Employee">select *from employeewhere id = #{id}</select>
接口7 编辑员工信息
/*** 编辑员工信息** @param employeeDTO* @return*/@Tag(name = "员工相关接口")@Operation(summary = "编辑员工信息")@PutMappingpublic Result update(@RequestBody EmployeeDTO employeeDTO) {log.info("编辑员工信息:{}", employeeDTO);employeeService.update(employeeDTO);return Result.success();}
1 @RequestBoby的作用:将HTTP请求体当中的参数数据自动绑定到指定的java对象当中。
public Result update(@RequestBody EmployeeDTO employeeDTO)
2 调用Service服务层的方法
employeeService.update(employeeDTO);
3 service服务层
/*** 编辑员工信息** @param employeeDTO*/void update(EmployeeDTO employeeDTO);
4 service的实现类
/*** 编辑员工信息** @param employeeDTO*/@Overridepublic void update(EmployeeDTO employeeDTO) {//使用BeanUtils.copyProperties方法将employeeDto中的属性值复制到employee对象中Employee employee = new Employee();BeanUtils.copyProperties(employeeDTO, employee);employeeMapper.update(employee);}
使用BeanUtils实现对象拷贝
BeanUtils.copyProperties(employeeDTO, employee);
在使用Mapper接口当中的更新方法
employeeMapper.update(employee);
Mapper接口方法
/*** 修改员工信息* @param employee*/@AutoFill(OperationType.UPDATE)void update(Employee employee);
MapperXML文件SQL实现
<!-- 信息的更新 --><update id="update">update employee<set><if test="name != null">name = #{name},</if><if test="phone != null">phone = #{phone},</if><if test="sex != null">sex = #{sex},</if><if test="idNumber != null">id_number = #{idNumber},</if><if test="status != null">status = #{status},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{update}</if></set>where id = #{id}</update>
二 菜品相关接口
三层架构模式(Controller控制层、Service业务层、Mapper持久层)
控制层DishController
package com.sky.controller.admin;import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
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.*;import java.util.List;@Slf4j
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
public class DishController {@Autowiredprivate DishService dishService;/*** 新增菜品** @param dishDTO* @return*/@ApiOperation("新增菜品")@PostMappingpublic Result save(@RequestBody DishDTO dishDTO) {log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);return Result.success();}/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@ApiOperation("菜品分页查询")@GetMapping("/page")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}/*** 菜品删除** @param ids* @return*/@ApiOperation("菜品删除")@DeleteMappingpublic Result delete(@RequestParam List<Long> ids) {log.info("菜品删除:{}", ids);dishService.deleteBatch(ids);return Result.success();}/*** 根据id查询菜品和对应的口味信息* @param id* @return*/@ApiOperation("根据id查询菜品和口味信息")@GetMapping("/{id}")public Result<DishVO> getById(@PathVariable Long id) {log.info("根据id查询菜品信息:{}", id);DishVO dishVO = dishService.getByIdWithFlavor(id);return Result.success(dishVO);}/*** 修改菜品* @param dishDTO* @return*/@ApiOperation("修改菜品")@PutMappingpublic Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}", dishDTO);dishService.updateWithFlavor(dishDTO);return Result.success();}/*** 根据分类id查询菜品* @param categoryId* @return*/@ApiOperation("根据分类id查询菜品")@GetMapping("/list")public Result<List<Dish>> list(Long categoryId) {log.info("根据分类id查询菜品:{}", categoryId);// 获取这个分类下的菜品List<Dish> dishVOList = dishService.list(categoryId);return Result.success(dishVOList);}}
接口1 新增菜品
/*** 新增菜品** @param dishDTO* @return*/@ApiOperation("新增菜品")@PostMappingpublic Result save(@RequestBody DishDTO dishDTO) {log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);return Result.success();}
1 @RequestBoby将请求体当中的参数自动封装到DishDTO
public Result save(@RequestBody DishDTO dishDTO)
2 调用Service层当中的新增菜品的方法
dishService.saveWithFlavor(dishDTO);
3 Service服务层
/*** 新增菜品和对应的口味数据** @param dishDTO*/void saveWithFlavor(DishDTO dishDTO);
4 重写方法
/*** 新增菜品和对应的口味数据** @param dishDTO*/@Override@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//向菜品表中插入1条数据dishMapper.insert(dish);//获取菜品idLong dishId = dish.getId();//向口味表插入n条数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && !flavors.isEmpty()) {flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});// 向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);log.info("新增菜品:{},新增口味:{}", dish, flavors);}}
首先对象拷贝将传递过来的DishDTO菜品对象拷贝给Dish
Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);
调用Mapper接口的方法插入数据
dishMapper.insert(dish);
Mapper接口
/*** 插入数据** @return*/// 公共字段自动填充@AutoFill(OperationType.INSERT)void insert(Dish dish);
MapperXML文件(这里需要写上useGenerateKeys="true" keyProperty="id" 可以高效地解决插入数据后获取自增主键的需求,避免了手动查询最新主键的繁琐操作。因为后面还有对这个菜品添加对应的口味数据。)
<!-- useGenerateKeys获取主键值 --><insert id="insert" useGeneratedKeys="true" keyProperty="id">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})</insert>
向口味表当中添加数据(DTODish对象当中还有口味信息,在之前的拷贝过程当中已经将一些其他的信息拷贝完成,现在只需要将其中的口味进行存储即可)
//获取菜品idLong dishId = dish.getId();//向口味表插入n条数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && !flavors.isEmpty()) {flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});// 向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);log.info("新增菜品:{},新增口味:{}", dish, flavors);}
首先需要遍历口味将菜品的dishId给每种口味赋值上,然后执行插入操作
dishFlavorMapper.insertBatch(flavors);
调用口味的Mapper接口(传入的是口味的集合,实现批量插入)
/*** 批量插入口味数据** @param flavors*/void insertBatch(List<DishFlavor> flavors);
口味的MapperXML文件
<!-- 实现口味的批量插入 --><insert id="insertBatch">insert into dish_flavor (dish_id, name, value) values<foreach collection="flavors" item="flavor" separator=",">(#{flavor.dishId},#{flavor.name},#{flavor.value})</foreach></insert>
接口2 修改菜品
/*** 修改菜品* @param dishDTO* @return*/@ApiOperation("修改菜品")@PutMappingpublic Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}", dishDTO);dishService.updateWithFlavor(dishDTO);return Result.success();}
1 RequestBoby将HTTP请求当中的参数数据封装成DishDTO对象
public Result update(@RequestBody DishDTO dishDTO)
2 调用Service服务层的更新方法
dishService.updateWithFlavor(dishDTO);
3 Service服务层接口方法
/*** 根据id修改菜品的基本信息和口味信息** @param dishDTO*/void updateWithFlavor(DishDTO dishDTO);
4 Service服务层实现类重写方法
/*** 根据id修改菜品的基本信息和口味信息** @param dishDTO*/@Transactional@Overridepublic void updateWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//将dishDTO中的数据拷贝到dish中(这里知识修改菜品表的基本信息,不需要对口味进行修改)// 修改菜品表基本信息dishMapper.update(dish);// 删除原有口味信息dishFlavorMapper.deleteByDishId(dishDTO.getId());// 重新添加口味信息List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && !flavors.isEmpty()) {flavors.forEach(dishFlavor -> dishFlavor.setDishId(dishDTO.getId()));dishFlavorMapper.insertBatch(flavors);}log.info("修改菜品:{}", dishDTO);}
实现对菜品基础信息的拷贝(这里暂时不会对口味进行处理)
Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//将dishDTO中的数据拷贝到dish中(这里知识修改菜品表的基本信息,不需要对口味进行修改)
修改菜品的基础信息
// 修改菜品表基本信息dishMapper.update(dish);
Mapper层接口
/*** 根据id修改菜品基本数据* @param dish*/@AutoFill(OperationType.UPDATE)void update(Dish dish);
Mapper层XML文件
<!-- 根据id动态修改菜品数据 --><update id="update">update dish<set><if test="name != null">name = #{name},</if><if test="categoryId != null">category_id = #{categoryId},</if><if test="price != null">price = #{price},</if><if test="image != null">image = #{image},</if><if test="description != null">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>
删除原有口味信息(根据菜品id)
// 删除原有口味信息dishFlavorMapper.deleteByDishId(dishDTO.getId());
口味Mapper层接口
/*** 根据菜品id删除对应的口味数据** @param dishId*/@Delete("delete from dish_flavor where dish_id = #{dishId}")void deleteByDishId(Long dishId);
口味的重新添加,先将口味的属性赋值上菜品的Id,便于匹配。然后再调用方法添加口味
// 重新添加口味信息List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && !flavors.isEmpty()) {flavors.forEach(dishFlavor -> dishFlavor.setDishId(dishDTO.getId()));dishFlavorMapper.insertBatch(flavors);}log.info("修改菜品:{}", dishDTO);
Mapper
/*** 批量插入口味数据** @param flavors*/void insertBatch(List<DishFlavor> flavors);
<!-- 实现口味的批量插入 --><insert id="insertBatch">insert into dish_flavor (dish_id, name, value) values<foreach collection="flavors" item="flavor" separator=",">(#{flavor.dishId},#{flavor.name},#{flavor.value})</foreach></insert>
接口3 删除菜品
/*** 菜品删除** @param ids* @return*/@ApiOperation("菜品删除")@DeleteMappingpublic Result delete(@RequestParam List<Long> ids) {log.info("菜品删除:{}", ids);dishService.deleteBatch(ids);return Result.success();}
1 @RequestParam将url路径的查询参数获取
public Result delete(@RequestParam List<Long> ids)
2 调用Service层的删除方法
dishService.deleteBatch(ids);
3 Service接口
/*** 菜品的批量删除功能** @param ids*/void deleteBatch(List<Long> ids);
4 Service实现类
/*** 批量删除菜品** @param ids*/@Transactional@Overridepublic void deleteBatch(List<Long> ids) {//判断当前菜品是否在售for (Long id : ids) {Dish dish = dishMapper.getById(id);//根据id查询菜品数据(添加方法)if (dish.getStatus() == 1) {//如果菜品处于启售状态,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}}//判断当前菜品是否被套餐关联List<Long> dishIds = setMealDishMapper.getSetMealIdsByDishIds(ids);//根据菜品id查询套餐id(添加方法)if (dishIds != null && !dishIds.isEmpty()) {//如果存在关联的套餐,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}//批量删除菜品dishMapper.deleteByIds(ids);//批量删除菜品对应的口味数据dishFlavorMapper.deleteByDishIds(ids);}
根据菜品的id进行遍历,获取其状态值判断是否在售,在售卖则不能删除
//判断当前菜品是否在售for (Long id : ids) {Dish dish = dishMapper.getById(id);//根据id查询菜品数据(添加方法)if (dish.getStatus() == 1) {//如果菜品处于启售状态,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}}
Mapper接口的方法
/*** 根据主键获取菜品信息** @param id* @return*/@Select("select * from dish where id = #{id}")Dish getById(Long id);
根据菜品的id查询套餐的id,如果存在关联的套餐则不能删除
//判断当前菜品是否被套餐关联List<Long> dishIds = setMealDishMapper.getSetMealIdsByDishIds(ids);//根据菜品id查询套餐id(添加方法)if (dishIds != null && !dishIds.isEmpty()) {//如果存在关联的套餐,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}
Mapper接口
/*** 根据菜品id查询套餐id** @param dishIds* @return*/List<Long> getSetMealIdsByDishIds(List<Long> dishIds);
MapperXML文件
<!-- 根据菜品id查询对应的套餐id --><select id="getSetMealIdsByDishIds" resultType="java.lang.Long">select setmeal_id from setmeal_dish where dish_id in<foreach collection="dishIds" item="dishId" open="(" close=")" separator=",">#{dishId}</foreach></select>
判断结束,成功校验则对菜品进行删除
//批量删除菜品dishMapper.deleteByIds(ids);//批量删除菜品对应的口味数据dishFlavorMapper.deleteByDishIds(ids);
Mapper接口
/*** 根据菜品id集合批量删除菜品数据* @param ids*/void deleteByIds(List<Long> ids);
MapperXML文件
<!-- 根据菜品id批量删除 --><delete id="deleteByIds">delete from dish where id in<foreach collection="ids" open="(" close=")" separator="," item="id">#{id}</foreach></delete>
Mapper接口
/*** 根据菜品id批量删除口味数据** @param ids*/void deleteByDishIds(List<Long> ids);
MapperXML文件
<!-- 实现口味的批量删除 --><delete id="deleteByDishIds">delete from dish_flavor where dish_id in<foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach></delete>
接口4 根据分类的id查询菜品
每个菜品都有一个类id
/*** 根据分类id查询菜品* @param categoryId* @return*/@ApiOperation("根据分类id查询菜品")@GetMapping("/list")public Result<List<Dish>> list(Long categoryId) {log.info("根据分类id查询菜品:{}", categoryId);// 获取这个分类下的菜品List<Dish> dishVOList = dishService.list(categoryId);return Result.success(dishVOList);}
1 方法传递的是这个分类的id返回的菜品构成的集合
public Result<List<Dish>> list(Long categoryId)
2 调用Service层的方法
// 获取这个分类下的菜品List<Dish> dishVOList = dishService.list(categoryId);
3 Service层
/*** 根据分类id查询菜品返回菜品的集合* @param categoryId* @return*/List<Dish> list(Long categoryId);
4 Service层实现类
/*** 根据分类id查询菜品获取菜品的集合** @param categoryId* @return*/@Overridepublic List<Dish> list(Long categoryId) {Dish dish = Dish.builder().categoryId(categoryId).status(StatusConstant.ENABLE).build();return dishMapper.list(dish);}
将菜品分类id和起售状态传递给Dish
Dish dish = Dish.builder().categoryId(categoryId).status(StatusConstant.ENABLE).build();
Mapper接口
/*** 动态查询菜品* @param dish* @return*/List<Dish> list(Dish dish);
MapperXML文件
MyBatis 通过 反射 或 Get 方法 访问 Dish
对象的属性(如 name
、categoryId
、status
)
<!-- 动态条件查询菜品 --><select id="list" resultType="com.sky.entity.Dish" parameterType="Dish">select * from dish<where><if test="name != null and name != ''">and name like concat('%', #{name}, '%')</if><if test="categoryId != null">and category_id = #{categoryId}</if><if test="status != null">and status = #{status}</if></where>order by create_time desc</select>
接口5 菜品分页查询
/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@ApiOperation("菜品分页查询")@GetMapping("/page")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}
1 传递DishPageQueryDTO对象
public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO)
@Data
public class DishPageQueryDTO implements Serializable {private int page;private int pageSize;private String name;//分类idprivate Integer categoryId;//状态 0表示禁用 1表示启用private Integer status;}
2 调用Service接口的方法
PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);
3 Service接口
/*** 菜品分页查询** @param dishPageQueryDTO* @return*/PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);
4 Service实现类
/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@Overridepublic PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);return new PageResult(page.getTotal(), page.getResult());}
作用:
使用 MyBatis 分页插件 PageHelper 启动分页,自动拦截后续的 SQL 查询并添加分页逻辑(LIMIT
子句)。
参数:
dishPageQueryDTO.getPage()
:当前页码(从 第1页 开始)。
dishPageQueryDTO.getPageSize()
:每页记录数(如 10、20)。
底层原理:
PageHelper 通过 ThreadLocal 保存分页参数,后续的 第一个 SQL 查询会被自动分页。
PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
调用Mapper持久层接口的分页查询
Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);
Mapper持久层接口
/*** 菜品分页查询** @param dishPageQueryDTO* @return*/Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);
Mapper持久层XML文件
MyBatis 通过 反射 或 Get 方法 访问 DishVO
对象的属性
<!-- 菜品分页查询 --><select id="pageQuery" resultType="com.sky.vo.DishVO">select d.*,c.name as categoryNamefrom dish dleft join category c on d.category_id = c.id<where><if test="name != null and name != ''">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>
接口6 根据id查询菜品和口味信息
/*** 根据id查询菜品和对应的口味信息** @param id* @return*/@ApiOperation("根据id查询菜品和口味信息")@GetMapping("/{id}")public Result<DishVO> getById(@PathVariable Long id) {log.info("根据id查询菜品信息:{}", id);DishVO dishVO = dishService.getByIdWithFlavor(id);return Result.success(dishVO);}
1 @PathVariable 获取url路径上的参数id
public Result<DishVO> getById(@PathVariable Long id)
2 调用Service业务层的方法
DishVO dishVO = dishService.getByIdWithFlavor(id);
3 Service业务层接口
/*** 根据id查询菜品的基本信息和口味信息** @param id* @return*/DishVO getByIdWithFlavor(Long id);
4 Service业务层实现类
/*** 根据id查询菜品和对应的口味数据** @param id* @return*/@Overridepublic DishVO getByIdWithFlavor(Long id) {// 根据id查询菜品数据Dish dish = dishMapper.getById(id);// 根据id查询口味数据List<DishFlavor> dishFlavorList = dishFlavorMapper.getByDishId(id);// 将查询结果封装到VO中DishVO dishVO = new DishVO();BeanUtils.copyProperties(dish, dishVO);//将dish中的数据拷贝到dishVO中dishVO.setFlavors(dishFlavorList);return dishVO;}
根据id查询菜品/根据id查询口味
// 根据id查询菜品数据Dish dish = dishMapper.getById(id);// 根据id查询口味数据List<DishFlavor> dishFlavorList = dishFlavorMapper.getByDishId(id);
Mapper持久层
/*** 根据主键id获取菜品信息** @param id* @return*/@Select("select * from dish where id = #{id}")Dish getById(Long id);
/*** 根据菜品id查询对应的口味数据** @param dishId* @return*/@Select("select * from dish_flavor where dish_id = #{dishId}")List<DishFlavor> getByDishId(Long dishId);
将数据全部封装到DishVO当中(该类包含菜品的基础信息和口味信息)
// 将查询结果封装到VO中DishVO dishVO = new DishVO();BeanUtils.copyProperties(dish, dishVO);//将dish中的数据拷贝到dishVO中dishVO.setFlavors(dishFlavorList);
最后将其返回
return dishVO;
三 套餐管理相关接口
三层架构模式(Controller控制层、Service业务层、Mapper持久层)
控制层SetMealController
接口1 新增套餐
/*** 新增套餐** @param setmealDTO* @return*/@PostMapping@ApiOperation("新增套餐")public Result save(@RequestBody SetmealDTO setmealDTO) {log.info("新增套餐:{}", setmealDTO);setMealService.saveWithDish(setmealDTO);return Result.success();}
1 RequestBoby获取HTTP请求请求体当中的参数将其封装为Set MealDTO对象
public Result save(@RequestBody SetmealDTO setmealDTO)
2 调用Service层的方法
setMealService.saveWithDish(setmealDTO);
3 Service接口
/*** 新增套餐** @param setmealDTO*/void saveWithDish(SetmealDTO setmealDTO);
4 Service接口实现类
/*** 新增套餐,同时需要保存套餐和菜品的关联关系** @param setmealDTO*/@Transactional@Overridepublic void saveWithDish(SetmealDTO setmealDTO) {log.info("新增套餐:{}", setmealDTO);Setmeal setmeal = new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);// 向套餐表插入数据setmealMapper.insert(setmeal);// 获取生成的套餐idLong setmealId = setmeal.getId();log.info("套餐id:{}", setmealId);List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmealId);log.info("套餐id:{}", setmealId);});//保存套餐和菜品的关联关系setMealDishMapper.insertBatch(setmealDishes);}
将DTO的对象参数拷贝到实体类Setmeal当中
Setmeal setmeal = new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);
再调用Mapper接口的方法将数据插入(这里处理的是套餐的信息)
setmealMapper.insert(setmeal);
Mapper持久层(这里使用切面实现对时间公共字段的处理)
/*** 新增套餐* @param setmeal*/@AutoFill(OperationType.INSERT)void insert(Setmeal setmeal);
MapperXML文件(这里需要写上useGenerateKeys="true" keyProperty="id" 可以高效地解决插入数据后获取自增主键的需求,避免了手动查询最新主键的繁琐操作。)
<!-- 新增套餐 --><insert id="insert" parameterType="setmeal" useGeneratedKeys="true" keyProperty="id">insert into setmeal (id, category_id, name, price, status, description, image, create_time, update_time,create_user, update_user)values (#{id}, #{categoryId}, #{name}, #{price}, #{status}, #{description}, #{image}, #{createTime},#{updateTime}, #{createUser}, #{updateUser})</insert>
获取生成的套餐id
Long setmealId = setmeal.getId();
通过 foreach
循环为每个 SetmealDish
对象设置套餐 ID
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmealId);log.info("套餐id:{}", setmealId);});
将
setMealDishMapper.insertBatch(setmealDishes);
Mapper持久层接口
/*** 批量插入套餐菜品关系* @param setmealDishes*/void insertBatch(List<SetmealDish> setmealDishes);
MapperXML文件
<!-- 批量保存套餐和菜品的关联关系 --><insert id="insertBatch" parameterType="list">insert into setmeal_dish (setmeal_id, dish_id,name,price,copies)values<foreach collection="setmealDishes" item="setmealDish" separator=",">(#{setmealDish.setmealId},#{setmealDish.dishId},#{setmealDish.name},#{setmealDish.price},#{setmealDish.copies})</foreach></insert>
接口2 修改套餐
/*** 修改套餐** @param setmealDTO* @return*/@PutMapping@ApiOperation("修改套餐")public Result update(@RequestBody SetmealDTO setmealDTO) {log.info("修改套餐:{}", setmealDTO);setMealService.update(setmealDTO);return Result.success();}
1 @RequestBoby获取HTTP请求当中的参数将其封装到SetmealDTO当中
public Result update(@RequestBody SetmealDTO setmealDTO)
2 调用Service层的方法
setMealService.update(setmealDTO);
3 Service业务层接口
/*** 修改套餐** @param setmealDTO*/void update(SetmealDTO setmealDTO);
4 Service接口实现类
/*** 修改套餐** @param setmealDTO*/@Overridepublic void update(SetmealDTO setmealDTO) {Setmeal setmeal = new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);// 1.修改套餐表,执行更新操作updatesetmealMapper.update(setmeal);//2.获取套餐idLong setmealId = setmealDTO.getId();// 3.删除套餐和菜品的关联关系delete,操作setmeal_dish表setMealDishMapper.deleteBySetmealId(setmealId);List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmealId);});// 4.重新插入套餐和菜品的关联关系insert,操作setmeal_dish表setMealDishMapper.insertBatch(setmealDishes);}
将传递过来的DTO对象拷贝给实体对象
Setmeal setmeal = new Setmeal();BeanUtils.copyProperties(setmealDTO, setmeal);
将套餐表上的数据修改
setmealMapper.update(setmeal);
Mapper层接口
/*** 修改套餐表** @param setmeal*/@AutoFill(OperationType.UPDATE)void update(Setmeal setmeal);
MapperXML文件(这里的根据id是通过反射从传递的对象当中获取的)
<!-- 修改套餐表 --><update id="update">update setmeal<set><if test="name != null">name = #{name},</if><if test="categoryId != null">category_id = #{categoryId},</if><if test="price != null">price = #{price},</if><if test="image != null">image = #{image},</if><if test="description != null">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>
从DTO对象当中获取套餐的id值
Long setmealId = setmealDTO.getId();
删除原有关联数据:根据套餐ID删除原有菜品关联记录。
setMealDishMapper.deleteBySetmealId(setmealId);
设置新关联数据:将新提交的菜品对象与当前套餐ID绑定。
List<SetmealDish> setmealDishes = setmealDTO.getSetmealDishes();setmealDishes.forEach(setmealDish -> {setmealDish.setSetmealId(setmealId);});
批量插入新关联数据:将绑定后的菜品数据插入数据库。
setMealDishMapper.insertBatch(setmealDishes);
调用Mapper持久层接口
/*** 批量插入套餐菜品关系* @param setmealDishes*/void insertBatch(List<SetmealDish> setmealDishes);
MapperXML文件
<!-- 批量保存套餐和菜品的关联关系 --><insert id="insertBatch" parameterType="list">insert into setmeal_dish (setmeal_id, dish_id,name,price,copies)values<foreach collection="setmealDishes" item="setmealDish" separator=",">(#{setmealDish.setmealId},#{setmealDish.dishId},#{setmealDish.name},#{setmealDish.price},#{setmealDish.copies})</foreach></insert>
接口3 套餐分页查询
/*** 分页查询** @param setmealPageQueryDTO* @return*/@ApiOperation("套餐分页查询")@GetMapping("/page")public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO) {log.info("套餐分页查询:{}", setmealPageQueryDTO);PageResult pageResult = setMealService.pageQuery(setmealPageQueryDTO);return Result.success(pageResult);}
1 调用Service方法
PageResult pageResult = setMealService.pageQuery(setmealPageQueryDTO);
2 Service业务层
/*** 分页查询** @param setmealPageQueryDTO* @return*/PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
3 接口实现类
/*** 分页查询套餐** @param setmealPageQueryDTO* @return*/@Overridepublic PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {int pageNum = setmealPageQueryDTO.getPage();int pageSize = setmealPageQueryDTO.getPageSize();PageHelper.startPage(pageNum, pageSize);Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO);return new PageResult(page.getTotal(), page.getResult());}
作用:
使用 MyBatis 分页插件 PageHelper 启动分页,自动拦截后续的 SQL 查询并添加分页逻辑(LIMIT
子句)。
参数:
setmealPageQueryDTO.getPage()
:当前页码(从 第1页 开始)。
setmealPageQueryDTO.getPageSize()
:每页记录数(如 10、20)。
底层原理:
PageHelper 通过 ThreadLocal 保存分页参数,后续的 第一个 SQL 查询会被自动分页。
Mapper接口
/*** 分页查询** @param setmealPageQueryDTO* @return*/Page<SetmealVO> pageQuery(SetmealPageQueryDTO setmealPageQueryDTO);
MapperXML文件
<!-- 套餐分页查询 --><select id="pageQuery" resultType="com.sky.vo.SetmealVO">selects.*,c.name categoryNamefromsetmeal sleft joincategory cons.category_id = c.id<where><if test="name != null">and s.name like concat('%',#{name},'%')</if><if test="status != null">and s.status = #{status}</if><if test="categoryId != null">and s.category_id = #{categoryId}</if></where>order by s.create_time desc</select>
接口4 套餐批量删除
/*** 批量删除套餐** @param ids* @return*/@DeleteMapping@ApiOperation("批量删除套餐")public Result delete(@RequestParam List<Long> ids) {log.info("批量删除套餐:{}", ids);setMealService.deleteBatch(ids);return Result.success();}
1 RequestParam从HTTP请求当中获取数据封装到集合当中
public Result delete(@RequestParam List<Long> ids)
2 调用Service业务层当中的方法
setMealService.deleteBatch(ids);
3 Service业务层接口
/*** 批量删除套餐** @param ids*/void deleteBatch(List<Long> ids);
4 Service业务层接口实现类
/*** 批量删除套餐** @param ids*/@Transactional@Overridepublic void deleteBatch(List<Long> ids) {ids.forEach(id -> {Setmeal setmeal = setmealMapper.getById(id);//(新增方法)if (setmeal.getStatus() == 1) {//如果菜品处于启售状态,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}});ids.forEach(setmealId -> {//删除套餐数据setmealMapper.deleteById(setmealId);//(新增方法)//删除套餐和菜品的关联数据setMealDishMapper.deleteBySetmealId(setmealId);//(新增方法)});}
遍历套餐当中的菜品是否处于售卖状态,处于售卖状态则不能删除并抛出异常
ids.forEach(id -> {Setmeal setmeal = setmealMapper.getById(id);//(新增方法)if (setmeal.getStatus() == 1) {//如果菜品处于启售状态,则不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}});
满足需求则将其数据删除
ids.forEach(setmealId -> {//删除套餐数据setmealMapper.deleteById(setmealId);//(新增方法)//删除套餐和菜品的关联数据setMealDishMapper.deleteBySetmealId(setmealId);//(新增方法)});
根据套餐id删除套餐数据
setmealMapper.deleteById(setmealId);
Mapper持久层
/*** 根据id删除套餐** @param id*/@Select("delete from setmeal where id = #{id}")void deleteById(Long id);
删除套餐与菜品的关联数据
setMealDishMapper.deleteBySetmealId(setmealId);
Mapper持久层
/*** 根据套餐id删除套餐菜品关系** @param setmealId*/@Delete("delete from setmeal_dish where setmeal_id = #{setmealId}")void deleteBySetmealId(Long setmealId);
接口5 根据id查询套餐(回显)
/*** 根据id查询套餐-用于修改界面回显数据** @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询套餐")public Result<SetmealVO> getById(@PathVariable Long id) {log.info("根据id查询套餐:{}", id);SetmealVO setmealVO = setMealService.getByIdWithDish(id);return Result.success(setmealVO);}
1 @PathVariable获取请求路径当中的id信息
public Result<SetmealVO> getById(@PathVariable Long id)
2 调用Service业务层接口将查询数据接收封装
SetmealVO setmealVO = setMealService.getByIdWithDish(id);
3 Service业务层接口
/*** 根据id查询套餐和关联的菜品数据** @param id* @return*/SetmealVO getByIdWithDish(Long id);
4 Service业务层实现类
/*** 根据id查询套餐和套餐菜品的关联关系** @param id* @return*/@Overridepublic SetmealVO getByIdWithDish(Long id) {Setmeal setmeal = setmealMapper.getById(id);List<SetmealDish> setmealDishes = setMealDishMapper.getBySetmealId(id);//(新增方法)SetmealVO setmealVO = new SetmealVO();BeanUtils.copyProperties(setmeal, setmealVO);setmealVO.setSetmealDishes(setmealDishes);return setmealVO;}
先根据套餐id查询套餐
Setmeal setmeal = setmealMapper.getById(id);
Mapper层
/*** 根据id查询套餐** @param id* @return*/@Select("select * from setmeal where id = #{id}")Setmeal getById(Long id);
根据套餐id查询套餐中菜品
List<SetmealDish> setmealDishes = setMealDishMapper.getBySetmealId(id);
Mapper层
/*** 根据套餐id查询套餐菜品关系** @param id* @return*/@Select("select * from setmeal_dish where setmeal_id = #{id}")List<SetmealDish> getBySetmealId(Long id);
将查询到的数据封装到setmealVO当中给前端返回
SetmealVO setmealVO = new SetmealVO();BeanUtils.copyProperties(setmeal, setmealVO);setmealVO.setSetmealDishes(setmealDishes);
接口6 套餐的启售与停售
/*** 套餐启售停售** @param status* @param id* @return*/@PostMapping("/status/{status}")@ApiOperation("套餐起售停售")public Result startOrStop(@PathVariable Integer status, Long id) {log.info("套餐起售停售:{},{}", status, id);setMealService.startOrStop(status, id);return Result.success();}
1 @PathVariable从请求url路径当中获取需要修改状态,id是通过请求的参数自动绑定
public Result startOrStop(@PathVariable Integer status, Long id)
2 调用Service业务层方法
setMealService.startOrStop(status, id);
3 Service接口
/*** 套餐启售停售** @param status* @param id*/void startOrStop(Integer status, Long id);
4 Service实现类
/*** 启售与停售** @param status* @param id*/@Overridepublic void startOrStop(Integer status, Long id) {//起售套餐时,判断套餐内是否有停售菜品,有停售菜品提示"套餐内包含未启售菜品,无法启售"if (status == StatusConstant.ENABLE) {//select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = ?List<Dish> dishList = dishMapper.getBySetmealId(id);if (dishList != null && !dishList.isEmpty()) {dishList.forEach(dish -> {if (StatusConstant.DISABLE == dish.getStatus()) {throw new SetmealEnableFailedException(MessageConstant.SETMEAL_ENABLE_FAILED);}});}}Setmeal setmeal = Setmeal.builder().id(id).status(status).build();setmealMapper.update(setmeal);}
Mapper接口查询菜品
/*** 根据套餐id查询菜品** @param id* @return*/@Select("select a.* from dish a left join setmeal_dish b on a.id = b.dish_id where b.setmeal_id = #{setmealId}")List<Dish> getBySetmealId(Long id);