基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(六)

Myth丶恋晨 2024-04-17 10:08 140阅读 0赞

新增菜品

    • 1.1 需求分析与设计
      • 1.1.1 产品原型
      • 1.1.2 接口设计
      • 1.1.3 表设计
    • 2.2 代码开发
      • 2.2.1 文件上传实现
      • 2.2.2 新增菜品实现
    • 2.3 功能测试

1.1 需求分析与设计

1.1.1 产品原型

后台系统中可以管理菜品信息,通过 新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类,并且需要上传菜品图片。

新增菜品原型:

在这里插入图片描述

当填写完表单信息, 点击”保存”按钮后, 会提交该表单的数据到服务端, 在服务端中需要接受数据, 然后将数据保存至数据库中。

业务规则:

  • 菜品名称必须是唯一的
  • 菜品必须属于某个分类下,不能单独存在
  • 新增菜品时可以根据情况选择菜品的口味
  • 每个菜品必须对应一张图片

1.1.2 接口设计

根据上述原型图先粗粒度设计接口,共包含3个接口。

接口设计:

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

接下来细粒度分析每个接口,明确每个接口的请求方式、请求路径、传入参数和返回值。

1. 根据类型查询分类

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

2. 文件上传

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

3. 新增菜品

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

1.1.3 表设计

通过原型图进行分析:

在这里插入图片描述

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


















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

1). 菜品表:dish














































































字段名 数据类型 说明 备注
id bigint 主键 自增
name varchar(32) 菜品名称 唯一
category_id bigint 分类id 逻辑外键
price decimal(10,2) 菜品价格
image varchar(255) 图片路径
description varchar(255) 菜品描述
status int 售卖状态 1起售 0停售
create_time datetime 创建时间
update_time datetime 最后修改时间
create_user bigint 创建人id
update_user bigint 最后修改人id

2). 菜品口味表:dish_flavor




































字段名 数据类型 说明 备注
id bigint 主键 自增
dish_id bigint 菜品id 逻辑外键
name varchar(32) 口味名称
value varchar(255) 口味值

2.2 代码开发

2.2.1 文件上传实现

因为在新增菜品时,需要上传菜品对应的图片(文件),包括后绪其它功能也会使用到文件上传,故要实现通用的文件上传接口。

文件上传,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发抖音、发朋友圈都用到了文件上传功能。

实现文件上传服务,需要有存储的支持,那么我们的解决方案将以下几种:

  1. 直接将图片保存到服务的硬盘(springmvc中的文件上传)

    1. 优点:开发便捷,成本低
    2. 缺点:扩容困难
  2. 使用分布式文件系统进行存储

    1. 优点:容易实现扩容
    2. 缺点:开发复杂度稍大(有成熟的产品可以使用,比如:FastDFS,MinIO)
  3. 使用第三方的存储服务(例如OSS)

    1. 优点:开发简单,拥有强大功能,免维护
    2. 缺点:付费

在本项目选用阿里云的OSS服务进行文件存储。
在这里插入图片描述

实现步骤:

1). 定义OSS相关配置

在sky-server模块

application-dev.yml

  1. sky:
  2. alioss:
  3. endpoint: oss-cn-hangzhou.aliyuncs.com
  4. access-key-id: **************************
  5. access-key-secret: *******************
  6. bucket-name: sky-take-out

application.yml

  1. spring:
  2. profiles:
  3. active: dev #设置环境
  4. sky:
  5. alioss:
  6. endpoint: ${
  7. sky.alioss.endpoint}
  8. access-key-id: ${
  9. sky.alioss.access-key-id}
  10. access-key-secret: ${
  11. sky.alioss.access-key-secret}
  12. bucket-name: ${
  13. sky.alioss.bucket-name}

2). 读取OSS配置

在sky-common模块中,已定义

  1. package com.sky.properties;
  2. import lombok.Data;
  3. import org.springframework.boot.context.properties.ConfigurationProperties;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. @ConfigurationProperties(prefix = "sky.alioss")
  7. @Data
  8. public class AliOssProperties {
  9. private String endpoint;
  10. private String accessKeyId;
  11. private String accessKeySecret;
  12. private String bucketName;
  13. }

3). 生成OSS工具类对象

在sky-server模块

  1. package com.sky.config;
  2. /**
  3. * 配置类,用于创建AliOssUtil对象
  4. */
  5. @Configuration
  6. @Slf4j
  7. public class OssConfiguration {
  8. @Bean
  9. @ConditionalOnMissingBean
  10. public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties){
  11. log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);
  12. return new AliOssUtil(aliOssProperties.getEndpoint(),
  13. aliOssProperties.getAccessKeyId(),
  14. aliOssProperties.getAccessKeySecret(),
  15. aliOssProperties.getBucketName());
  16. }
  17. }

其中,AliOssUtil.java已在sky-common模块中定义

  1. package com.sky.utils;
  2. @Data
  3. @AllArgsConstructor
  4. @Slf4j
  5. public class AliOssUtil {
  6. private String endpoint;
  7. private String accessKeyId;
  8. private String accessKeySecret;
  9. private String bucketName;
  10. /**
  11. * 文件上传
  12. *
  13. * @param bytes
  14. * @param objectName
  15. * @return
  16. */
  17. public String upload(byte[] bytes, String objectName) {
  18. // 创建OSSClient实例。
  19. OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
  20. try {
  21. // 创建PutObject请求。
  22. ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
  23. } catch (OSSException oe) {
  24. System.out.println("Caught an OSSException, which means your request made it to OSS, "
  25. + "but was rejected with an error response for some reason.");
  26. System.out.println("Error Message:" + oe.getErrorMessage());
  27. System.out.println("Error Code:" + oe.getErrorCode());
  28. System.out.println("Request ID:" + oe.getRequestId());
  29. System.out.println("Host ID:" + oe.getHostId());
  30. } catch (ClientException ce) {
  31. System.out.println("Caught an ClientException, which means the client encountered "
  32. + "a serious internal problem while trying to communicate with OSS, "
  33. + "such as not being able to access the network.");
  34. System.out.println("Error Message:" + ce.getMessage());
  35. } finally {
  36. if (ossClient != null) {
  37. ossClient.shutdown();
  38. }
  39. }
  40. //文件访问路径规则 https://BucketName.Endpoint/ObjectName
  41. StringBuilder stringBuilder = new StringBuilder("https://");
  42. stringBuilder
  43. .append(bucketName)
  44. .append(".")
  45. .append(endpoint)
  46. .append("/")
  47. .append(objectName);
  48. log.info("文件上传到:{}", stringBuilder.toString());
  49. return stringBuilder.toString();
  50. }
  51. }

4). 定义文件上传接口

在sky-server模块中定义接口

  1. package com.sky.controller.admin;
  2. /**
  3. * 通用接口
  4. */
  5. @RestController
  6. @RequestMapping("/admin/common")
  7. @Api(tags = "通用接口")
  8. @Slf4j
  9. public class CommonController {
  10. @Autowired
  11. private AliOssUtil aliOssUtil;
  12. /**
  13. * 文件上传
  14. * @param file
  15. * @return
  16. */
  17. @PostMapping("/upload")
  18. @ApiOperation("文件上传")
  19. public Result<String> upload(MultipartFile file){
  20. log.info("文件上传:{}",file);
  21. try {
  22. //原始文件名
  23. String originalFilename = file.getOriginalFilename();
  24. //截取原始文件名的后缀 dfdfdf.png
  25. String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
  26. //构造新文件名称
  27. String objectName = UUID.randomUUID().toString() + extension;
  28. //文件的请求路径
  29. String filePath = aliOssUtil.upload(file.getBytes(), objectName);
  30. return Result.success(filePath);
  31. } catch (IOException e) {
  32. log.error("文件上传失败:{}", e);
  33. }
  34. return Result.error(MessageConstant.UPLOAD_FAILED);
  35. }
  36. }

2.2.2 新增菜品实现

1). 设计DTO类

在sky-pojo模块中

  1. package com.sky.dto;
  2. @Data
  3. public class DishDTO implements Serializable {
  4. private Long id;
  5. //菜品名称
  6. private String name;
  7. //菜品分类id
  8. private Long categoryId;
  9. //菜品价格
  10. private BigDecimal price;
  11. //图片
  12. private String image;
  13. //描述信息
  14. private String description;
  15. //0 停售 1 起售
  16. private Integer status;
  17. //口味
  18. private List<DishFlavor> flavors = new ArrayList<>();
  19. }

2). Controller层

进入到sky-server模块

  1. package com.sky.controller.admin;
  2. /**
  3. * 菜品管理
  4. */
  5. @RestController
  6. @RequestMapping("/admin/dish")
  7. @Api(tags = "菜品相关接口")
  8. @Slf4j
  9. public class DishController {
  10. @Autowired
  11. private DishService dishService;
  12. /**
  13. * 新增菜品
  14. *
  15. * @param dishDTO
  16. * @return
  17. */
  18. @PostMapping
  19. @ApiOperation("新增菜品")
  20. public Result save(@RequestBody DishDTO dishDTO) {
  21. log.info("新增菜品:{}", dishDTO);
  22. dishService.saveWithFlavor(dishDTO);//后绪步骤开发
  23. return Result.success();
  24. }
  25. }

3). Service层接口

  1. package com.sky.service;
  2. import com.sky.dto.DishDTO;
  3. import com.sky.entity.Dish;
  4. public interface DishService {
  5. /**
  6. * 新增菜品和对应的口味
  7. *
  8. * @param dishDTO
  9. */
  10. public void saveWithFlavor(DishDTO dishDTO);
  11. }

4). Service层实现类

  1. package com.sky.service.impl;
  2. @Service
  3. @Slf4j
  4. public class DishServiceImpl implements DishService {
  5. @Autowired
  6. private DishMapper dishMapper;
  7. @Autowired
  8. private DishFlavorMapper dishFlavorMapper;
  9. /**
  10. * 新增菜品和对应的口味
  11. *
  12. * @param dishDTO
  13. */
  14. @Transactional
  15. public void saveWithFlavor(DishDTO dishDTO) {
  16. Dish dish = new Dish();
  17. BeanUtils.copyProperties(dishDTO, dish);
  18. //向菜品表插入1条数据
  19. dishMapper.insert(dish);//后绪步骤实现
  20. //获取insert语句生成的主键值
  21. Long dishId = dish.getId();
  22. List<DishFlavor> flavors = dishDTO.getFlavors();
  23. if (flavors != null && flavors.size() > 0) {
  24. flavors.forEach(dishFlavor -> {
  25. dishFlavor.setDishId(dishId);
  26. });
  27. //向口味表插入n条数据
  28. dishFlavorMapper.insertBatch(flavors);//后绪步骤实现
  29. }
  30. }
  31. }

5). Mapper层

DishMapper.java中添加

  1. /**
  2. * 插入菜品数据
  3. *
  4. * @param dish
  5. */
  6. @AutoFill(value = OperationType.INSERT)
  7. void insert(Dish dish);

在/resources/mapper中创建DishMapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
  4. <mapper namespace="com.sky.mapper.DishMapper">
  5. <insert id="insert" useGeneratedKeys="true" keyProperty="id">
  6. insert into dish (name, category_id, price, image, description, create_time, update_time, create_user,update_user, status)
  7. values (#{name}, #{categoryId}, #{price}, #{image}, #{description}, #{createTime}, #{updateTime}, #{createUser}, #{updateUser}, #{status})
  8. </insert>
  9. </mapper>

DishFlavorMapper.java

  1. package com.sky.mapper;
  2. import com.sky.entity.DishFlavor;
  3. import java.util.List;
  4. @Mapper
  5. public interface DishFlavorMapper {
  6. /**
  7. * 批量插入口味数据
  8. * @param flavors
  9. */
  10. void insertBatch(List<DishFlavor> flavors);
  11. }

在/resources/mapper中创建DishFlavorMapper.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
  4. <mapper namespace="com.sky.mapper.DishFlavorMapper">
  5. <insert id="insertBatch">
  6. insert into dish_flavor (dish_id, name, value) VALUES
  7. <foreach collection="flavors" item="df" separator=",">
  8. (#{df.dishId},#{df.name},#{df.value})
  9. </foreach>
  10. </insert>
  11. </mapper>

2.3 功能测试

进入到菜品管理—>新建菜品

在这里插入图片描述

由于没有实现菜品查询功能,所以保存后,暂且在表中查看添加的数据。

dish表:

在这里插入图片描述

dish_flavor表:
在这里插入图片描述

测试成功。

后记
????美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! ???

发表评论

表情:
评论列表 (有 0 条评论,140人围观)

还没有评论,来说两句吧...

相关阅读