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

苍穹外卖笔记-07-菜品管理-增加、删除、修改、查询分页还有菜品起售或停售状态

菜品管理

  • 1 新增菜品
    • 1.1 需求分析与设计
    • 1.2 代码开发
      • 文件上传
      • 新增菜品实现
    • 1.3 功能测试
  • 2 菜品分页查询
    • 2.1 需求分析和设计
    • 2.2 代码开发
      • 设计DTO类
      • 设计VO类
      • Controller层
      • Service层
      • Mapper层
    • 2.3 功能测试
  • 3 删除菜品
    • 3.1 需求分析和设计
    • 3.2 代码开发
      • Controller层
      • Service层
      • Mapper层
    • 3.3 功能测试
  • 4 修改菜品
    • 4.1 需求分析和设计
    • 4.2 根据id查询菜品代码开发
      • Controller层
      • Service层
    • 4.3 修改菜品代码开发
      • Controller层
      • Service层接口
      • Mapper层
    • 4.4 功能测试
  • 5 菜品起售或停售状态
    • 5.1 需求分析和设计
      • 5.1.1 页面原型
      • 5.1.2 接口设计
    • 5.2 代码开发
      • Controller
      • Service
      • Mapper层
    • 5.3 功能测试

1 新增菜品

1.1 需求分析与设计

详见资料中的产品原型和业务规则。HTML的静态网页已经包含所有信息。
产品原型
在这里插入图片描述
业务规则
在这里插入图片描述
接口设计
接口设计:

  • 根据类型查询分类(已完成)
  • 文件上传
  • 新增菜品

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
表设计

在这里插入图片描述新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。所以在新增菜品时,涉及到两个表:

表名说明
dish菜品表
dish_flavor菜品口味表

1). 菜品表:dish

字段名数据类型说明备注
idbigint主键自增
namevarchar(32)菜品名称唯一
category_idbigint分类id逻辑外键
pricedecimal(10,2)菜品价格
imagevarchar(255)图片路径
descriptionvarchar(255)菜品描述
statusint售卖状态1起售 0停售
create_timedatetime创建时间
update_timedatetime最后修改时间
create_userbigint创建人id
update_userbigint最后修改人id

在这里插入图片描述

2). 菜品口味表:dish_flavor

字段名数据类型说明备注
idbigint主键自增
dish_idbigint菜品id逻辑外键
namevarchar(32)口味名称
valuevarchar(255)口味值

在这里插入图片描述

1.2 代码开发

文件上传

  1. 直接将图片保存到服务的硬盘(springmvc中的文件上传)
    1. 优点:开发便捷,成本低
    2. 缺点:扩容困难
  2. 使用分布式文件系统进行存储
    1. 优点:容易实现扩容
    2. 缺点:开发复杂度稍大(有成熟的产品可以使用,比如:FastDFS,MinIO)
  3. 使用第三方的存储服务(例如OSS)
    1. 优点:开发简单,拥有强大功能,免维护
    2. 缺点:付费

我们使用OSS服务进行文件存储
实现步骤:

1). 定义OSS相关配置

在sky-server模块

application-dev.yml

sky:alioss:endpoint: yoursaccess-key-id: yoursaccess-key-secret: yoursbucket-name: yours

application.yml

spring:profiles:active: dev    #设置环境
sky:alioss:endpoint: ${sky.alioss.endpoint}access-key-id: ${sky.alioss.access-key-id}access-key-secret: ${sky.alioss.access-key-secret}bucket-name: ${sky.alioss.bucket-name}

以下是我使用阿里云OSS存储的文件。
在这里插入图片描述
rescources中添加
在这里插入图片描述在这里插入图片描述

2). 读取OSS配置

在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 lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 配置类,用于创建AliOssUtil对象*/
@Configuration
@Slf4j
public class OssConfiguration {@Bean@ConditionalOnMissingBeanpublic AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}
}

其中,AliOssUtil.java已在sky-common模块中定义
定义了一个名为AliOssUtil的工具类,用于与阿里云OSS(Object Storage Service)交互,实现文件的上传。以下是关键点的解释:

  1. 类属性:
  • endpoint: 阿里云OSS服务的访问域名。
  • accessKeyId: 阿里云账号的Access Key ID,用于身份验证。
  • accessKeySecret: 阿里云账号的Access Key Secret,用于身份验证。
  • bucketName: 存储空间名称。
  1. 上传方法:
  • upload(byte[] bytes, String objectName):接收一个字节数组(代表文件内容)和一个对象名称,然后将文件上传到OSS。
    使用OSSClientBuilder创建OSSClient实例,这是与OSS服务交互的主要对象。
  • putObject方法用于上传文件,传入存储空间名称、对象名称和ByteArrayInputStream,后者由字节数组创建。
  • 异常处理:捕获OSSException和ClientException,分别处理服务端和客户端可能出现的问题。
    最后,关闭OSSClient以释放资源。
  1. URL构建:
  • 方法返回一个字符串,表示上传文件后的访问URL。URL由bucketName、endpoint和objectName拼接而成。
  1. 数据库连接字符串(未完的代码片段):
  • 提供了一个JDBC数据库连接字符串,用于连接MySQL数据库。包含了数据库的主机、端口、数据库名以及相关连接参数。

!! !请注意,暴露accessKeyId和accessKeySecret这样的敏感信息在生产环境中是不安全的。通常,这些信息应存储在环境变量或配置服务器中,并在运行时注入到应用程序中。

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/ObjectNameStringBuilder stringBuilder = new StringBuilder("https://");stringBuilder.append(bucketName).append(".").append(endpoint).append("/").append(objectName);log.info("文件上传到:{}", stringBuilder.toString());return stringBuilder.toString();}
}

4). 定义文件上传接口

在sky-server模块中定义接口

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 {@Autowiredprivate AliOssUtil aliOssUtil;/*** 文件上传* @param file* @return*/@PostMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file){log.info("文件上传:{}",file);try {//原始文件名String originalFilename = file.getOriginalFilename();//截取原始文件名的后缀   dfdfdf.pngString 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);}
}

其中,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/ObjectNameStringBuilder stringBuilder = new StringBuilder("https://");stringBuilder.append(bucketName).append(".").append(endpoint).append("/").append(objectName);log.info("文件上传到:{}", stringBuilder.toString());return stringBuilder.toString();}
}

4). 定义文件上传接口

在sky-server模块中的controller.admin定义接口 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 {@Autowiredprivate AliOssUtil aliOssUtil;/*** 文件上传* @param file* @return*/@PostMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file){log.info("文件上传:{}",file);try {//原始文件名String originalFilename = file.getOriginalFilename();//截取原始文件名的后缀   dfdfdf.pngString 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);}
}

新增菜品实现

设计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;//菜品分类idprivate Long categoryId;//菜品价格private BigDecimal price;//图片private String image;//描述信息private String description;//0 停售 1 起售private Integer status;//口味private List<DishFlavor> flavors = new ArrayList<>();
}

2). Controller层

进入到sky-server模块

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;
import java.util.Set;/*** 菜品管理*/
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {@Autowiredprivate DishService dishService;/*** 新增菜品** @param dishDTO* @return*/@PostMapping@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO) {log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);//后绪步骤开发return Result.success();}
}

3). Service层接口

package com.sky.service;import com.sky.dto.DishDTO;
import com.sky.entity.Dish;public interface DishService {/*** 新增菜品和对应的口味** @param dishDTO*/public void saveWithFlavor(DishDTO dishDTO);}

4). Service层实现类

package com.sky.service.impl;@Service
@Slf4j
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;@Autowiredprivate DishFlavorMapper dishFlavorMapper;/*** 新增菜品和对应的口味** @param dishDTO*/@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//向菜品表插入1条数据dishMapper.insert(dish);//后绪步骤实现//获取insert语句生成的主键值Long dishId = dish.getId();List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && flavors.size() > 0) {flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishId);});//向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);//后绪步骤实现}}}

5). Mapper层

DishMapper.java中添加

	/*** 插入菜品数据** @param dish*/@AutoFill(value = OperationType.INSERT)void insert(Dish dish);

在/resources/mapper中创建DishMapper.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.DishMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into dish (name, category_id, price, image, description, create_time, update_time, create_user,update_user, status)values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser}, #{status})</insert>
</mapper>

DishFlavorMapper.java

package com.sky.mapper;import com.sky.entity.DishFlavor;
import java.util.List;@Mapper
public interface DishFlavorMapper {/*** 批量插入口味数据* @param flavors*/void insertBatch(List<DishFlavor> flavors);}

在/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="insertBatch">insert into dish_flavor (dish_id, name, value) VALUES<foreach collection="flavors" item="df" separator=",">(#{df.dishId},#{df.name},#{df.value})</foreach></insert>
</mapper>

1.3 功能测试

不管是接口测试还是前后端测试

最后看控制台信息,添加成功!
在这里插入图片描述

在这里插入图片描述

2 菜品分页查询

2.1 需求分析和设计

产品原型
业务规则
见资料静态HTML网页
接口设计
见ApiFox网站

2.2 代码开发

设计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;private Integer categoryId; //分类idprivate Integer status; //状态 0表示禁用 1表示启用}

设计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;//菜品分类idprivate 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<>();
}

Controller层

根据接口定义创建DishController的page分页查询方法:

	/*** 菜品分页查询** @param dishPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("菜品分页查询")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);//后绪步骤定义return Result.success(pageResult);}

Service层

在 DishService 中扩展分页查询方法:

	/*** 菜品分页查询** @param dishPageQueryDTO* @return*/PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO);

在 DishServiceImpl 中实现分页查询方法:

	/*** 菜品分页查询** @param dishPageQueryDTO* @return*/public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);//后绪步骤实现return new PageResult(page.getTotal(), page.getResult());}

Mapper层

在 DishMapper 接口中声明 pageQuery 方法:

	/*** 菜品分页查询** @param dishPageQueryDTO* @return*/Page<DishVO> pageQuery(DishPageQueryDTO dishPageQueryDTO);

在 DishMapper.xml 中编写SQL:

<select id="pageQuery" resultType="com.sky.vo.DishVO">select d.* , c.name as categoryName from dish d left outer join category c on d.category_id = c.id<where><if test="name != null">and d.name like concat('%',#{name},'%')</if><if test="categoryId != null">and d.category_id = #{categoryId}</if><if test="status != null">and d.status = #{status}</if></where>order by d.create_time desc
</select>

2.3 功能测试

在这里插入图片描述
在这里插入图片描述

3 删除菜品

3.1 需求分析和设计

菜品原型和业务规则
详见资料中的页面原型

接口设计
详见Apifox

表设计
在这里插入图片描述
注意事项:

  • 在dish表中删除菜品基本数据时,同时,也要把关联在dish_flavor表中的数据一块删除。
  • setmeal_dish表为菜品和套餐关联的中间表。
  • 若删除的菜品数据关联着某个套餐,此时,删除失败。
  • 若要删除套餐关联的菜品数据,先解除两者关联,再对菜品进行删除。

3.2 代码开发

Controller层

根据删除菜品的接口定义在DishController中创建方法:

	/*** 菜品批量删除** @param ids* @return*/@DeleteMapping@ApiOperation("菜品批量删除")public Result delete(@RequestParam List<Long> ids) {log.info("菜品批量删除:{}", ids);dishService.deleteBatch(ids);//后绪步骤实现return Result.success();}

Service层

在DishService接口中声明deleteBatch方法:

	/*** 菜品批量删除** @param ids*/void deleteBatch(List<Long> ids);

在DishServiceImpl中实现deleteBatch方法:

    @Autowiredprivate SetmealDishMapper setmealDishMapper;/*** 菜品批量删除** @param ids*/@Transactional//事务public void deleteBatch(List<Long> ids) {//判断当前菜品是否能够删除---是否存在起售中的菜品??for (Long id : ids) {Dish dish = dishMapper.getById(id);//后绪步骤实现if (dish.getStatus() == StatusConstant.ENABLE) {//当前菜品处于起售中,不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);}}//判断当前菜品是否能够删除---是否被套餐关联了??List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(ids);if (setmealIds != null && setmealIds.size() > 0) {//当前菜品被套餐关联了,不能删除throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);}//删除菜品表中的菜品数据for (Long id : ids) {dishMapper.deleteById(id);//后绪步骤实现//删除菜品关联的口味数据dishFlavorMapper.deleteByDishId(id);//后绪步骤实现}}

Mapper层

在DishMapper中声明getById方法,并配置SQL:

	/*** 根据主键查询菜品** @param id* @return*/@Select("select * from dish where id = #{id}")Dish getById(Long id);

创建SetmealDishMapper,声明getSetmealIdsByDishIds方法,并在xml文件中编写SQL:

package com.sky.mapper;import com.sky.entity.SetmealDish;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;@Mapper
public interface SetmealDishMapper {/*** 根据菜品id查询对应的套餐id** @param dishIds* @return*///select setmeal_id from setmeal_dish where dish_id in (1,2,3,4)List<Long> getSetmealIdsByDishIds(List<Long> dishIds);
}

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="getSetmealIdsByDishIds" resultType="java.lang.Long">select setmeal_id from setmeal_dish where dish_id in<foreach collection="dishIds" item="dishId" separator="," open="(" close=")">#{dishId}</foreach></select>
</mapper>

在DishMapper.java中声明deleteById方法并配置SQL:

	/*** 根据主键删除菜品数据** @param id*/@Delete("delete from dish where id = #{id}")void deleteById(Long id);

在DishFlavorMapper中声明deleteByDishId方法并配置SQL:

    /*** 根据菜品id删除对应的口味数据* @param dishId*/@Delete("delete from dish_flavor where dish_id = #{dishId}")void deleteByDishId(Long dishId);

3.3 功能测试

详略得当!
在这里插入图片描述

4 修改菜品

4.1 需求分析和设计

产品原型和接口设计
详见资料中的页面原型和ApiFox中的接口设计

接口:

  • 根据id查询菜品
  • 根据类型查询分类(已实现)
  • 文件上传(已实现)
  • 修改菜品

我们只需要实现根据id查询菜品修改菜品两个接口,接下来,我们来重点分析这两个接口。

4.2 根据id查询菜品代码开发

Controller层

根据id查询菜品的接口定义在DishController中创建方法:

    /*** 根据id查询菜品** @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询菜品")public Result<DishVO> getById(@PathVariable Long id) {log.info("根据id查询菜品:{}", id);DishVO dishVO = dishService.getByIdWithFlavor(id);//后绪步骤实现return Result.success(dishVO);}

Service层

在DishService接口中声明getByIdWithFlavor方法:

	/*** 根据id查询菜品和对应的口味数据** @param id* @return*/DishVO getByIdWithFlavor(Long id);

在DishServiceImpl中实现getByIdWithFlavor方法:

	/*** 根据id查询菜品和对应的口味数据** @param id* @return*/public DishVO getByIdWithFlavor(Long id) {//根据id查询菜品数据Dish dish = dishMapper.getById(id);//根据菜品id查询口味数据List<DishFlavor> dishFlavors = dishFlavorMapper.getByDishId(id);//后绪步骤实现//将查询到的数据封装到VODishVO dishVO = new DishVO();BeanUtils.copyProperties(dish, dishVO);dishVO.setFlavors(dishFlavors);return dishVO;}

Mapper层
在DishFlavorMapper中声明getByDishId方法,并配置SQL:

    /*** 根据菜品id查询对应的口味数据* @param dishId* @return*/@Select("select * from dish_flavor where dish_id = #{dishId}")List<DishFlavor> getByDishId(Long dishId);

4.3 修改菜品代码开发

Controller层

根据修改菜品的接口定义在DishController中创建方法:

	/*** 修改菜品** @param dishDTO* @return*/@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}", dishDTO);dishService.updateWithFlavor(dishDTO);return Result.success();}

Service层接口

在DishService接口中声明updateWithFlavor方法:

	/*** 根据id修改菜品基本信息和对应的口味信息** @param dishDTO*/void updateWithFlavor(DishDTO dishDTO);

在DishServiceImpl中实现updateWithFlavor方法:

	/*** 根据id修改菜品基本信息和对应的口味信息** @param dishDTO*/public void updateWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO, dish);//修改菜品表基本信息dishMapper.update(dish);//删除原有的口味数据dishFlavorMapper.deleteByDishId(dishDTO.getId());//重新插入口味数据List<DishFlavor> flavors = dishDTO.getFlavors();if (flavors != null && flavors.size() > 0) {flavors.forEach(dishFlavor -> {dishFlavor.setDishId(dishDTO.getId());});//向口味表插入n条数据dishFlavorMapper.insertBatch(flavors);}}

Mapper层

在DishMapper中,声明update方法:

	/*** 根据id动态修改菜品数据** @param dish*/@AutoFill(value = OperationType.UPDATE)void update(Dish dish);

并在DishMapper.xml文件中编写SQL:

<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>

4.4 功能测试

在这里插入图片描述
在这里插入图片描述

5 菜品起售或停售状态

5.1 需求分析和设计

5.1.1 页面原型

在菜品管理列表页面,可以对某个菜品进行启售或者售操作。停售的菜品不能售卖,启售的菜品可以售卖。如果某个菜品状态为正常,则按钮显示为 “停售”,如果菜品状态为停售,则按钮显示为"起售"。

在这里插入图片描述
业务规则
启售/停售
菜品停售,则包含菜品的套餐同时停售。
菜品停售,则包含菜品的套餐同时停售。
菜品停售,则包含菜品的套餐同时停售。

【启售/停售】,菜品被停售后,前台不展示
点【启售/停售】弹出确认弹窗,操作成功提示 操作成功,失败提示 操作失败。

5.1.2 接口设计

在这里插入图片描述
1). 路径参数携带状态值。

2). 同时,把id传递过去,明确对哪个菜品进行操作。

3). 返回数据code状态是必须,其它是非必须。

5.2 代码开发

Controller

在sky-server模块中,根据接口设计中的请求参数形式对应的在 DishController 中创建启用禁用员工账号的方法:

/*** 菜品起售停售* @param status* @param id* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("菜品起售停售")
public Result<String> startOrStop(@PathVariable Integer status, Long id){dishService.startOrStop(status,id);return Result.success();
}

Service

DishService接口中声明startOrStop方法

/*** 菜品起售停售* @param status* @param id
*/
void startOrStop(Integer status, Long id);

DishServiceImpl中实现startOrStop方法。

/*** 菜品起售停售** @param status* @param id
*/
@Transactional
public void startOrStop(Integer status, Long id) {Dish dish = Dish.builder().id(id).status(status).build();dishMapper.update(dish);if (status == StatusConstant.DISABLE) {// 如果是停售操作,还需要将包含当前菜品的套餐也停售List<Long> dishIds = new ArrayList<>();dishIds.add(id);// select setmeal_id from setmeal_dish where dish_id in (?,?,?)List<Long> setmealIds = setmealDishMapper.getSetmealIdsByDishIds(dishIds);if (setmealIds != null && setmealIds.size() > 0) {for (Long setmealId : setmealIds) {Setmeal setmeal = Setmeal.builder().id(setmealId).status(StatusConstant.DISABLE).build();setmealMapper.update(setmeal);}}}
}

Mapper层

SetmealMapper已经实现了对根据id修改

/*** 根据id修改菜品** @param setmeal*/
@AutoFill(OperationType.UPDATE)
void update(Setmeal setmeal);

SetmealMapper.xml中添加一条对categoryId的判断。

<?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.SetmealMapper"><update id="update" parameterType="Setmeal">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="status != null">status = #{status},</if><if test="description != null">description = #{description},</if><if test="image != null">image = #{image},</if><if test="updateTime != null">update_time = #{updateTime},</if><if test="updateUser != null">update_user = #{updateUser}</if></set>where id = #{id}</update></mapper>

5.3 功能测试

在这里插入图片描述

http://www.lryc.cn/news/368104.html

相关文章:

  • oracle dataguard 从库 MRP 进程的状态是 WAIT_FOR_GAP
  • 【C语言】轻松拿捏-联合体
  • 基于Python定向爬虫技术对微博数据可视化设计与实现
  • 【QT5】<总览三> QT常用控件
  • Python中的生成器表达式(generator expression)
  • Responder工具
  • gitblit 环境搭建,服务器迁移记录
  • 硬盘坏了数据能恢复吗 硬盘数据恢复一般多少钱
  • 312. 戳气球 Hard
  • 推荐4个好用有趣的软件
  • GPT-4.0来袭:人工智能新纪元即将开启
  • Luminar Neo - AI智能修图软件超越PS和LR,简单易用又高效!
  • 【Linux】rsync远程数据同步工具使用
  • 以sqlilabs靶场为例,讲解SQL注入攻击原理【42-53关】
  • 单片机数码管时钟电路的设计
  • win10文件夹.git或者文件被隐藏的开启姿势
  • Paper速读-[Visual Prompt Multi-Modal Tracking]-Dlut.edu-CVPR2023
  • memory动态内存管理学习之unique_ptr
  • 1、项目介绍:为什么要做此项目。
  • 2024年6月7日第十五周下午学习英语六级大纲
  • 每日5题Day19 - LeetCode 91 - 95
  • wordpress里面嵌入哔哩哔哩视频的方法
  • Linux系统管理磁盘管理004
  • Flink窗口理论到实践
  • 279 基于matlab的粒子群集法对铁路电能质量控制系统的容量避行优化设计
  • 46-3 护网溯源 - 溯源报告编写
  • 微服务之基本介绍
  • 嘉立创面板制作不规则图案技巧
  • 如何使用Python中的collections模块提供的数据结构,如deque、Counter、OrderedDict等
  • 2024年道路安全员考试题库