苍穹外卖项目笔记(4)——菜品管理

菜品管理

主要功能模块:新建菜品、修改菜品、启用禁用菜品、菜品的分页查询、删除菜品

代码:GitHub - Echo0701/take-out

1 公共字段自动填充

       公共字段指的是业务表中有一些相同的字段,比如创建人、创建时间、修改人、修改时间等,我们在维护这些数据的时候,都需要为这几个字段来赋值,为避免代码重复,引入公共字段自动填充的内容。

1.1 问题分析

业务表中存在公共字段,导致 Java 中出现冗余的代码,后期如果进行变更,不方便维护

1.2 实现思路

明确上述字段的操作时机

  • 自定义注解 AutoFill ,用于标识需要进行公共字段自动填充的方法
  • 自定义切面类 AutoFillAspect ,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值 
  • 在  Mapper 的方法上加入 AutoFill 注解

【技术点】枚举(标识当前操作的类型)、注解、AOP、反射

1.3 代码开发

AutoFill.java

/*** 自定义注解,用于表示某个方法需要进行公共字段填充处理*/
@Target(ElementType.METHOD)  //指定注解只能加在方法上面
@Retention(RetentionPolicy.RUNTIME)  //指明注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,且可以在运行时通过反射获取到。这样的注解可以用来在运行时进行一些特殊的操作,例如动态生成代码、动态代理等public @interface AutoFill {//指定当前数据库的操作的类型:update insertOperationType value();
}

AutoFillAspect.java

@Aspect
@Component
@Slf4j  //记录日志
public class AutoFillAspect {//定义切入点:哪些类的哪些方法来进行拦截/*** 切入点*/@Pointcut("execution(* com.sky.mapper.*.*(..) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut() {}/*** 前置通知,在这个部分进行公共字段的赋值*///定义前置通知,因为要在 update 和 insert 之前为公共字段赋值//当匹配上切点表达式的时候就会执行我们这个通知的方法@Before("autoFillPointCut")public void autoFill(JoinPoint joinPoint) {log.info("开始进行公共字段的填充...");//1、获取到当前被拦截的方法上的数据库操作类型MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //方法签名对象AutoFill autoFill= signature.getMethod().getAnnotation(AutoFill.class);  //获得方法上的注解对象OperationType operationType = autoFill.value(); // 获得数据库操作类型//2、获取到当前被拦截的方法的参数--实体对象Object[] args= joinPoint.getArgs();if(args == null || args.length == 0) {return;}Object entity = args[0];//3、准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//4、根据当前的不同操作类型,为对应的属性通过反射来赋值if (operationType == OperationType.INSERT) {//为四个公共字段赋值try {//获得了四个公共字段的set方法
//                Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime",LocalDateTime.class);  为了避免方法拼写错误,所以统一用方法常量,定义在common包Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME,LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER,Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER,Long.class);//通过反射来为对象属性赋值setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {throw new RuntimeException(e);}} else if (operationType == OperationType.UPDATE) {//为两个公共字段赋值try {//获得了2个公共字段的set方法Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER,Long.class);//通过反射来为对象属性赋值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);} catch (Exception e) {throw new RuntimeException(e);}}}
}

2 新增菜品

2.1 需求分析和设计

产品原型

业务规则

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

接口设计 

① 根据类型查询分类

② 文件上传

③ 新增菜品

数据库设计

【逻辑外键】:数据库里并没有将这个外界关系真的给创建出来,而是通过我们的程序自己去维护这个字段,换句话说,数据库并不认为当前这个字段是一个外界,但在程序中会将其当作外键字段来处理

2.2 代码开发

2.2.1 文件上传

利用阿里云OSS来存储对象,完成文件上传

OssConfiguration.java

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.ConditionalOnBean;
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@ConditionalOnMissingBean  //保证整个spring容器里只有一个util对象,当没有util的条件下才会创建这个beanpublic AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {log.info("开始创建阿里云文件上传工具类对象:{}",aliOssProperties);return new AliOssUtil(aliOssProperties.getEndpoint(),aliOssProperties.getAccessKeyId(),aliOssProperties.getAccessKeySecret(),aliOssProperties.getBucketName());}
}

 application-dev.yml

sky:datasource:driver-class-name: com.mysql.cj.jdbc.Driverhost: localhostport: 3306database: sky_take_outusername: rootpassword: rootalioss:endpoint: https://oss-cn-hangzhou.aliyuncs.comaccess-key-id: LTAI5tLmKyefze8CGYs47HQtaccess-key-secret: MvO3k0AIAm4GSNujs0fLug4Chyu4sBbucket-name: cq-takeout-test

 application.yml

sky:jwt:# 设置jwt签名加密时使用的秘钥admin-secret-key: itcast# 设置jwt过期时间admin-ttl: 7200000# 设置前端传递过来的令牌名称admin-token-name: tokenalioss: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}

【代码逻辑】配置文件里配置了几个配置项,这几个配置项通过配置属性类来加载,然后又编写了配置类,通过这个配置类就可以创建出所需要的对象

上传文件的通用控制类 CommonController.java

/*** 通用接口*/
@RestController
@RequestMapping("/admin/common")
@Api(tags = "通用接口")
@Slf4j
public class CommonController {//这里的 file 要和前端接口请求的参数名相同private AliOssUtil aliOssUtil;/*** 文件上传* @param file* @return*/@PostMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file) {log.info("文件上传:{}",file);try {//原始文件名String originalFilename = file.getOriginalFilename();//截取原始文件后缀名String extension = originalFilename.substring(originalFilename.lastIndexOf("."));//构建新文件名称String  objectName = UUID.randomUUID().toString() + extension;//文件的请求路径String filePath = aliOssUtil.upload(file.getBytes(), objectName);return Result.success(filePath);} catch (IOException e) {log.info("文件上传失败:{}", e);}return Result.error(MessageConstant.UPLOAD_FAILED);}
}

2.2.2 新增菜品 

 DishController.java

/*** 菜品管理*/
@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();}
}

 DishServiceImpl.java

@Service
@Slf4j
public class DishServiceImpl implements DishService {@Autowiredprivate DishMapper dishMapper;private DishFlavorMapper dishFlavorMapper;/*** 新增菜品和口味* @param dishDTO*/@Transactionalpublic void saveWithFlavor(DishDTO dishDTO) {Dish dish = new Dish();BeanUtils.copyProperties(dishDTO,dish);//向菜品表插入一条数据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);}}
}

 DishFlavorMapper.java

@Mapper
public interface DishFlavorMapper {/*** 批量插入口味数据* @param flavors*/void insertBatch(List<DishFlavor> flavors);//因为要用到动态sql,所以写到映射文件里面去
}

  DishMapper.java

@Mapper
public interface DishMapper {/*** 根据分类id查询菜品数量* @param categoryId* @return*/@Select("select count(id) from dish where category_id = #{categoryId}")Integer countByCategoryId(Long categoryId);/*** 插入菜品数据* @param dish*/@AutoFill(value = OperationType.INSERT)void insert(Dish dish);//这里插入的数据很多,所以写到映射文件里面去
}

3 菜品分页查询

3.1 需求分析和设计

产品原型

业务规则

  • 根据页码展示菜品信息
  • 每页展示十条数据
  • 分页查询时可以根据需要输入菜品名称、菜品分类、菜品状态进行查询

接口设计

3.2 代码开发

① 根据菜品分页查询接口定义设计对应的DTO:

② 根据菜品分页查询接口定义设计对应的VO:

【注】把这个vo转换成json数据传到前端,前端就可以正常去显示 

DishController.java

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

DishServiceImpl.java

    /*** 菜品分页查询* @param dishPageQueryDTO* @return*/public PageResult pageQuery(DishPageQueryDTO dishPageQueryDTO) {//利用PageHelper插件来进行分页操作//开始分页PageHelper.startPage(dishPageQueryDTO.getPage(),dishPageQueryDTO.getPageSize());//开始查询Page<DishVO> page = dishMapper.pageQuery(dishPageQueryDTO);return new PageResult(page.getTotal(),page.getResult());}

4 删除菜品

4.1 需求分析和设计

产品原型

业务规则

  • 可以一次删除一个菜品,也可以批量删除菜品
  • 起售中的菜品不能删除
  • 被套餐关联的菜品不能删除
  • 删除菜品以后,关联的口味数据也需要删除掉 

接口设计

数据库设计

【注】setmeal_dish表:套餐和菜品关系表 

4.2 代码开发

DishServiceImpl.java

    /*** 菜品批量删除* @param ids*///加上事务注解,保证事务的一致性@Transactionalpublic 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);}}

DishController.java

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

5 修改菜品

5.1 需求分析和设计

产品原型

 接口设计

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

① 根据 id 查询菜品

② 修改菜品 

5.2 代码开发

DishController.java

    /*** 根据 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);}/*** 修改菜品* @param dishDTO* @return*/@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}",dishDTO);dishService.updateWithFlavor(dishDTO);return Result.success();}

DishServiceImpl.java

    /*** 根据 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;}/*** 根据 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);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/206886.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

赞!优雅的Python多环境管理神器!易上手易操作!

前言 Python 的不同版本之间常常存在依赖关系和兼容性问题&#xff0c;为了方便开发人员在 不同项目中使用不同的版本 。 如果大家使用过Python版本管理工具&#xff0c;肯定大多数人使用的都是Anaconda&#xff0c;它是一个优秀的数据科学开发环境&#xff0c;本身也提供了丰…

有一台电脑一部手机就可以在网上赚钱,这些项目你也可以学会

很多人都希望能够在家中或者闲暇的时候&#xff0c;能够在网上赚钱&#xff0c;而网络给了我们这样的可能。只要有一台电脑和一部手机&#xff0c;你就可以开始你的赚钱之旅。这些项目并不难&#xff0c;只要你肯学&#xff0c;就一定能够成功。 1、美工设计 这个副业主要是推荐…

centos无法进入系统之原因解决办法集合

前言 可爱的小伙伴们&#xff0c;由于精力有限&#xff0c;暂时整理了两类。如果没有你遇到的问题也没有关系&#xff0c;欢迎底下留言评论或私信&#xff0c;小编看到后第一时间帮助解决 一. Centos 7 LVM xfs文件系统修复 情况1&#xff1a; [sda] Assuming drive cache:…

909-2015-T3

文章目录 1.原题2.算法思想2.1.求树的高度2.2.求路径 3.关键代码4.完整代码5.输出结果 1.原题 试编写算法&#xff0c;求给定二叉树上从根节点到叶子节点的一条路径长度等于树的深度减一的路径&#xff08;即列出从根节点到该叶子节点的节点序列&#xff09;&#xff0c;若这样…

利用QRCode.js生成动态二维码页面

文章目录 QRCode.js简介HTML结构JavaScript生成动态二维码拓展功能1. 联系信息二维码2. Wi-Fi网络信息二维码 总结 &#x1f389;利用QRCode.js生成动态二维码页面 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x1f388;该系列文章专栏…

windows系统安装ubuntu22.04虚拟机

镜像文件准备 镜像文件 官网 企业开源和Linux | Ubuntu 镜像下载地址 https://cn.ubuntu.com/download/server/step1 选择合适的版本下载 虚拟机安装 文件-- 新建虚拟机 选择镜像 修改安装路径 修改大小&#xff0c;最好60g&#xff0c;大一点 设置用户信息 设置虚拟机网络…

Python通过selenium调用IE11浏览器报错解决方法

前提 正常安装Python 工具&#xff0c;selenium 包可以正常导入。IE浏览器驱动 IEDriverServer.exe 已经正确放置到已经添加path目录的文件下。 报错现象&#xff1a; 解决方法 打开浏览器进入 internet 选项 切换到安全页签 &#xff0c;去除“应用保护模式” 再次调用验证…

接口自动化中cookies的处理技术

一&#xff0c;理论知识 为什么有cookie和session&#xff1f; 因为http协议是一种无状态的协议&#xff0c;即每次服务端接受到客户端的请求时都时一个全新的请求&#xff0c;服务器并不知道客户端的请求记录&#xff0c;session和cookie主要目的就是弥补http的无状态特性 …

for,while,until语句

一、for循环 读取不同的变量值&#xff0c;用来逐个执行同一组命令&#xff0c;经常使用在已经知道要进行多少次循环的场景。 1、基本格式 for 变量名称(注意是名称&#xff0c;不是变量$等) [ in 名称范围 ] (可以不写)do 执行内容 若满足循环则做什么动作do…

“云浮云福保”暖心回归! 保障升级价格不变,医保个账可为全家缴费!

11月22日&#xff0c;2024年“云浮云福保”项目启动会在广东省云浮市迎宾馆成功举办。记者在会上获悉&#xff0c;“云浮云福保”是在云浮市医疗保障局、云浮市金融工作局、国家金融监督管理总局云浮监管分局指导下&#xff0c;的指导下&#xff0c;由中国人民财产保险股份有限…

怎么提高拍摄视频画质和清晰度?这几个方法一定要学会

一、提高拍摄视频画质和清晰度 1、分辨率 分辨率就是我们常说的480P、720P、1080P、4K等等&#xff0c;分辨率越大&#xff0c;在某种程度上视频也就越清晰。虽然原理是这样的&#xff0c;但在不同平台上传视频也会影响最终的画质。 比如超高分辨率的视频不适合在某音。因为…

Electronica上海 Samtec 验证演示 | FireFly™Micro Flyover System™

摘要/前言 在圆满结束的2023慕尼黑上海电子展上&#xff0c;Samtec虎家团队为观众带来了前所未有的丰富体验&#xff1a;产品展示、采访、Demo演示、抽奖互动~ 尤其是Demo演示&#xff0c;虎家工程师FAE Marcus为大家带来了数个精彩的产品与系统讲解演示。其中更不乏合作伙伴…