当前位置: 首页 > news >正文

苍穹外卖阶段性总结 (超详细版)

目录

一 管理端员工的相关接口

接口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 对象的属性(如 namecategoryIdstatus

    <!--  动态条件查询菜品  --><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);

相关文章:

  • 阿里HumanAIGC 团队开源实时数字人项目ChatAnyone
  • 俄罗斯方块-简单开发版
  • using用法整理
  • 高等数学同步测试卷 同济7版 试卷部分 上 做题记录 上册期中同步测试卷 B卷
  • c++ 类和动态内存分配
  • Java基础语法
  • 本地搭建MQTT服务器并进行设备控制(ESP32+MicroPython)
  • Qt6离线安装过程
  • HarmonyOS 笔记
  • 9.QT-显示类控件|Label|显示不同格式的文本|显示图片|文本对齐|自动换行|缩进|边距|设置伙伴(C++)
  • 2025.4.20总结
  • 基于尚硅谷FreeRTOS视频笔记——11—RTOS获取源码及源码简介
  • STM32基础教程——串口收发
  • YOLOv11改进——基于注意力机制和密集小目标增强型EVA模块的设计与实现
  • QML中的JSON 处理
  • VMware虚拟机走主机代理上网
  • SAP IAS云产品简介
  • 《猎豹夕阳》
  • 机器学习(神经网络基础篇)——个人理解篇6(概念+代码)———参数优化篇
  • 【AI图像创作变现】02工具推荐与差异化对比
  • 大幅加仓美的、茅台,买入小米,银华基金李晓星:看好港股与A股消费股
  • 一季度浙江实现生产总值22300亿元,同比增长6.0%
  • 全国登记在册民营企业超过5700万户
  • 南方将迎三轮降雨,两广旱区的“解渴雨”也要来了
  • 寻女19年的“棉花糖爸爸”明将办团圆宴,大女儿:妹妹是片区销售主管
  • 央媒关注微短剧如何探索精品化之路:从“悬浮”落回“现实”