黑马苍穹外卖3 菜品管理 AOP+反射+阿里云OSS+基本增删改查

菜品管理

公共字段自动填充

在这里插入图片描述
对员工,菜品,套餐、、的设置时间等,导致代码冗余。
使用切面来为这些方法统一设置
在这里插入图片描述
枚举:标识当前操作的类型(不同来类型操作的字段名不同)。反射(为公共字段赋值)
为什么要用反射赋值,不能直接赋值嘛?因为获取的实体类对象可能不一样,比如员工和菜品,要使用set方法赋值你就要强转为某个实体类对象,这样写这个就没意义了,所以获取方法对象通过反射来赋值。

1.创建自定义注解
annotation.AutoFill.java

@Target(ElementType.METHOD)//指定注解只能加载方法上
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {
    //通过枚举-指定当前属性OperationType
    //数据库操作类型OperationType:就两种 Update 和 Insert
    OperationType value();
}

2.切面类
aspect.AutoFillAspect.java

@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    //这个包里的类和方法,同时还的是加上注解的
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut() {
    }
    //前置通知,在sql执行前加上即(公共字段赋值)
    @Before("autoFillPointCut()")//当匹配上切点表达式的执行这个
    public void autoFill(JoinPoint joinPoint) {//插入链接点的参数值

        //获取到当前被栏截的方法上的数据库操作类型
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//从连接点获得方法签名对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象
        OperationType value = autoFill.value();//获得数据库操作类型

        //获取到当前被拦截的方法的参数--实体对象
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
        Object entity = args[0];//实体对象

        //准备赋值的数据
        LocalDateTime now = LocalDateTime.now();//时间
        Long currentId = BaseContext.getCurrentId();//用户ID

        //根据当前不同的操作类型,为对应的属性通过反射来赋值
        if (value == OperationType.INSERT) {
            //插入:为4个公共字段赋值
            try {
                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().getDec laredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                //通过反射赋值
                setCreateTime.invoke(entity, now);
                setUpdateTime.invoke(entity, now);
                setCreateUser.invoke(entity, currentId);
                setUpdateUser.invoke(entity, currentId);
            } catch (Exception e) {
                e.printStackTrace();
            }


        } else if (value == OperationType.UPDATE) {
            //更新:为2个赋值
            try {
                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);
            }
        }
    }
}

菜品-增删改查

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
文件上传接口,请求参数,固定的请求头。
在这里插入图片描述

阿里云

通过互联网对外提供的各种服务。
项目开发中可以直接调用的软甲服务(收费)

阿里云OSS

对象存储服务。通过网路哦存储和调用文本。图片。音频和视频等各种文件。
服务器本地不用存文件,OSS帮我们存储和管理。
在这里插入图片描述
使用步骤:
在这里插入图片描述
Bucket:用于存储对象的容器,所有对象必须隶属于某个存储空间

回归本项目文件上传:
配置阿里OSS文件上传的配置类:
application.yml

  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}

application-dev.yml里写具体的值

  alioss:
    endpoint: yourEndpoint
    access-key-id: yourAccessKeyId
    access-key-secret: yourAccessKeySecret 
    bucket-name: yourBucketName

文件上传需要工具类sky-common.src.main.java.com.sky.utils.AliOssUtil.java
类里有这四个属性,通过调用upload后能够返回图片网址
配置类用于创建AliOssUtil对象,给四个属性赋值,值在配置文件读过来。
config.OssConfiguration.java

@Configuration
public class OssConfiguration {

    /**
     * 提供aliyunoss工具类
     *
     * @param aliOssProperties
     * @return
     */
    @Bean //项目启动时就会调用方法创建对象
    @ConditionalOnMissingBean//保证Spring容器里只有一个Util对象,条件对象当没Bean时再创建
    public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
        return new AliOssUtil(aliOssProperties.getEndpoint(),
            aliOssProperties.getAccessKeyId(),
            aliOssProperties.getAccessKeySecret(),
            aliOssProperties.getBucketName());
    }

}

解释一下为什么不用依赖注入:因为该对象已经由于配置文件赋值了,当此对象作为方法传入的时候不是null,而且要搞明白ioc容器是为了得到创建对象的权利。
文件上传CommonController.java

@PostMapping("/upload")
    @ApiOperation("文件上传")
    public Result<String> upload(MultipartFile file) {//要返回阿里云上传网址
        log.info("文件上传:{}",file);
        try {
            //获取文件原始名
            String originalFilename = file.getOriginalFilename();
            //获取文件后缀名
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            //获取文件最终名字
            String fileName = UUID.randomUUID().toString() + extension;

            //获取访问路径

            String filePath = aliOssUtil.upload(file.getBytes(), fileName);
            return Result.success(filePath);
        } catch (IOException e) {
            log.error("文件上传失败:{}", e);
        }

        return Result.error(MessageConstant.UPLOAD_FAILED);
    }

在启动类已经@EnableTransactionManagement //开启注解方式的事务管理
开始新增菜品
DishServiceImpl.java

@Transactional//设计几多个数据表,需要保证数据一致性,需要事务注解--保证原子性,全成功或全失败
    @Override
    public void saveWithFlavor(DishDTO dishDTO) {

        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);
        //向菜品表里添加1条数据
        dishMapper.insert(dish);
        //为口味赋值菜品在数据库中的id,利用了主键返回
        Long id = dish.getId();

        List<DishFlavor> flavors = dishDTO.getFlavors();
        //可能用户并没有提交口味数据
        if (flavors != null && flavors.size() > 0) {
            //向口味表里添加多条数据
            flavors.forEach(dishFlavor -> dishFlavor.setDishId(id));
            dishFlavorMapper.insertBatch(flavors);//批量插入
        }

    }

动态sql插入

<insert id="insert" useGeneratedKeys="true" keyProperty="id"><!-- 产生的主键值会赋给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>

查—菜品分页查询

在这里插入图片描述
在这里插入图片描述
返回数据:设计两个表:DIsh表和分类表
Query:不是josn ,而是地址栏+?key=value来查

在这里插入图片描述
所以为什么要创建这个VO呢?:DTO是前传给后 vo是后传给前

@GetMapping("/page")
    @ApiOperation("菜品分页查询")
    public Result<PageResult> page(DishPageQueryDTO dto) {//传入不是json,不用注解
        PageResult pageResult = dishService.pageQuery(dto);
        return Result.success(pageResult);
    }
public PageResult pageQuery(DishPageQueryDTO dto) {
        PageHelper.startPage(dto.getPage(), dto.getPageSize());
        Page<DishVO> dishVOs = dishMapper.pageQuery(dto);//为了适应接口使用DishVO
        return new PageResult(dishVOs.getTotal(), dishVOs.getResult());
    }
Page<DishVO> pageQuery(DishPageQueryDTO dto);

依旧是动态sql
sql语句:左外链接:将左边的表的所有项与右边的表作连接
将菜和种类两个表按照种类id链接起来,以此获得种类名称

select d.*,c.name as categoryName from dish d left outer join category c on d.category_id=c.id

由于种类名称查出来也叫name,所以给字段起别名

<select id="pageQuery" resultType="com.sky.vo.DishVO">
        select d.*,c.name categoryName from dish d left join category c on d.category_id = c.id
        <where><!--毕竟动态sql。给它用where动态拼上DTO的3个属性 -->
            <if test="categoryId! =null">d.category_id=#{categoryId}</if>
            <if test="status!=null">d.status=#{status}</if>
            <if test="name!=null">d.name like concat('%',#{name},'%')</if>
        </where>
        order by d.create_time desc<!--根据创建时间降序 -->
    </select>

删—删除菜品

可以单个删个批量删
在售卖中不能删除
被套餐关联的不能删
删除菜品后,关联的口味也要删
在这里插入图片描述
菜、菜口味、套餐
在这里插入图片描述
Controller:

@ApiOperation("批量删除菜品")
    @DeleteMapping
    public Result delete(@RequestParam List<Long> ids) {//@RequestParam让Spring去解析字符串,然后将分割的字符封装到集合对象中
        dishService.deleteBatch(ids);//批量删除

        //更新缓存
        cleanCache("dish_*");

        return Result.success();
    }

Service:
1.判断能否删除—是否在售卖
2.判断能否删除—是否有套餐关联
3.删除菜品
4.删除关联口味

	@Transactional //事务注解
    @Override
    public void  deleteBatch(List<Long> ids) {
        //判断能否删除---是否在售卖
            //遍历数组id--查询菜品状态
//        for (Long id : ids) {
//            Dish dish = dishMapper.getById(id);
//            if (dish.getStatus() == StatusConstant.ENABLE){//处于起售中,不能删除
//                throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
//            }
//        }
        //优化
        List<Dish> dishes = dishMapper.queryUnsale(ids);
        if (dishes != null && dishes.size() > 0) {
            throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE);
        }

        //判断能否删除---是否有套餐关联
            //查套餐表是否有当前菜品
        List<Long> dishIds = setMealDishMapper.getSetmealIdsByDishIds(ids);
        if (dishIds != null && dishIds.size() > 0) {
            //有关联
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL);
        }

        //删除菜品
        dishMapper.deleteBatch(ids);

        //删除关联口味
        dishFlavorMapper.deleteBatchByDishIds(ids);
    }

(1)sql
按照菜id查全部信息,以此得到是否在售卖

@Select("select * from dish where id=#{id}")
    Dish getById(Long id);

(2)sql
// select setmeal id from setmeal dish where dish_id in (1,2,3,4)
List queryUnsale(List ids);传入List列表,用动态sql
SetMealDishMapper.xml

<select id="getSetmealIdsByDishIds" resultType="java.lang.Long">
        select setmeal_id from setmeal_dish where dish_id in
        <foreach collection="dishIds" separator="," item="id" open="(" close=")">
            #{id}
        </foreach>
    </select>

(3)删除菜—传入为列表

<delete id="deleteBatch">
        delete from dish where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

(4)删除口味—传入为列表

<delete id="deleteBatchByDishIds">
        delete from dish_flavor where dish_id in
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </delete>

改—修改菜品

1.查询ID菜品回显
2.查询分类(√)
3.图片上传(√)
4.更新数据
在这里插入图片描述
在这里插入图片描述

(1)ID查菜品信息,数据回显
用DishVO不用DishDTO的原因:DishDTO没有分类名称,而且DTO是接受数据,VO是返回数据。而且,按规定dto 和vo所要执行的功能不同,一个是接前端数据,一个是返回给前端数据的,规范一点。
VO是和前端的交互,DTO是内部传参,POJO是和数据库交互

@ApiOperation("根据id查询菜品")
    @GetMapping("/{id}")
    public Result<DishVO> getById(@PathVariable Long id) {//返回VO,因为还要返回口味信息
        DishVO dishVO = dishService.getByIdWithFlavor(id);
        return Result.success(dishVO);
    }
 @Override
    public DishVO getByIdWithFlavor(Long id) {
        //根据id查询菜品数据
        Dish dish = dishMapper.getById(id);
        //根据菜品id查询口味数据
        List<DishFlavor> flavors = dishFlavorMapper.getByDishId(id);
        //数据封装到VO
        DishVO dishVO = new DishVO();
        BeanUtils.copyProperties(dish, dishVO);
        dishVO.setFlavors(flavors);
        return dishVO;
    

(2)

@ApiOperation("修改菜品")
    @PutMapping
    public Result update(@RequestBody DishDTO dishDTO) {
        dishService.updateWithFlavor(dishDTO);

        //更新缓存
//        Set keys = redisTemplate.keys("dish_*");
//        redisTemplate.delete(keys);
        cleanCache("dish_*");

        return Result.success();
    }

修改菜品以及其口味数据
//先修改菜品信息, //再删除口味信息,//再添加口味信息

 public void updateWithFlavor(DishDTO dishDTO) {
        Dish dish = new Dish();
        BeanUtils.copyProperties(dishDTO, dish);
        //先修改菜品信息
        dishMapper.update(dish);
        Long id = dishDTO.getId();
        //再删除口味信息
        dishFlavorMapper.deleteBatchByDishIds(Collections.singletonList(id));
        //再添加口味信息
        List<DishFlavor> flavors = dishDTO.getFlavors();
        if (flavors != null && flavors.size() > 0) {
            flavors.forEach(dishFlavor -> dishFlavor.setDishId(id));
            //向口味表插入数据
            dishFlavorMapper.insertBatch(flavors);
        }

    }

当相关属性有值时再去修改,所以用的是动态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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大模型自然语言生成自动驾驶可编辑仿真场景(其一 共十篇)

第一篇&#xff1a;LLM greater scene summarize 第二篇&#xff1a;LLM simulation Test effect 第三篇&#xff1a;LLM simulation driving scenario flow work 第四篇&#xff1a;LLM Algorithm flow description 第五篇&#xff1a;Configure the environment and back…

k8s离线部署nginx

1. 拉取nginx离线包到本地 sudo docker save nginx:latest -o nginx.tar 2. 导入nginx image到k8s命名空间中 sudo ctr -n k8s.io images import nginx.tar 3. 编辑nginx.yaml apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deployment spec:selector:match…

yocto系列讲解[实战篇]94 - 添加libhybris库和测试示例

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 1. 概述2. 添加libhybris的recipe3.libhybris编译4.libhybris测试5.自制Android C++动态库6.自制Android C++动态库编译7.创建testhyb…

java基于ssm+jsp 高校二手交易平台

1前台首页功能模块 高校二手交易平台&#xff0c;在系统首页可以查看首页、商品信息、论坛信息、新闻资讯、我的、跳转到后台、客服等内容&#xff0c;如图1所示。 图1系统功能界面图 用户登录、用户注册&#xff0c;在注册页面可以填写账号、密码、姓名、手机、身份证等信息进…

故障诊断 | SABO-VMD-SVM轴承故障诊断(Matlab)

效果一览 文章概述 故障诊断 | SABO-VMD-SVM轴承故障诊断(Matlab) 模型描述 减法平均的优化算法(Subtraction-Average-Based Optimizer (SABO)),是于2023年提出的一种基于数学行为的智能优化算法,该算法具有寻优能力强,收敛速度快等特点。以最小包络熵、最小样本熵、最…

2024GLEE生活暨教育(上海)博览会,8月20-22日,国家会展中心(上海)

2024GLEE生活暨教育(上海)博览会将于8月20-22日在中国国家会展中心&#xff08;上海&#xff09;举行&#xff0c;博览会总面积近万平方米&#xff0c;设有美好生活和教育产品两大主力展区&#xff0c;全面覆盖婴幼儿、学龄前、小学、初中、高中、大学、中年、老年各个年龄段的…

机械师硬盘数据清空怎么办?机械师硬盘数据清空怎么恢复

机械师硬盘数据清空怎么恢复&#xff1f;随着数字化时代的到来&#xff0c;数据已成为我们生活和工作中不可或缺的一部分。然而&#xff0c;硬盘数据的意外清空往往会给我们带来极大的困扰。本文将探讨在机械师硬盘数据清空后&#xff0c;我们应该如何快速有效地恢复数据。 图片…

软件开发教学:在线教育系统源码解析及教育培训小程序搭建实战

本篇文章&#xff0c;笔者将以“从零开始的软件开发教学”为主题&#xff0c;详细解析在线教育系统的源码&#xff0c;并通过实际操作来搭建一个教育培训小程序。 一、在线教育系统概述 在线教育系统是一个综合性的网络平台&#xff0c;旨在通过互联网提供教育资源和服务。该系…

Docker开机自动重启及自动启动容器

Docker开机自动重启及自动启动容器 Windows开机自动重启设置容器自动启动 Windows开机自动重启 勾选 Start Docker Desktop when you sign in to your computer 设置容器自动启动 1.docker update 命令 Usage: docker update [OPTIONS] CONTAINER [CONTAINER...]Update co…

“迷你剧场新体验:探索短剧小程序系统的魅力!“

在移动互联网的时代背景下&#xff0c;短视频和短剧已经成为人们娱乐消费的新宠。短剧小程序系统&#xff0c;以其独特的互动形式、便捷的观看体验和丰富的内容生态&#xff0c;为迷你剧场带来了全新的体验。接下来将探讨这个系统的魅力所在&#xff0c;以及它如何改变着人们的…

plt绘制网格图

代码 obj "accu" for (epoch,lr) in config:with open(data/epoch_{}_lr_{}_Adam.pkl.format(epoch,lr),rb) as f:data pickle.load(f) plt.plot(range(1,epoch1),data[obj],labelflr{lr})plt.title(obj"-epoch") plt.xlabel("epoch"…

AMSR/ADEOS-II L1A Raw Observation Counts V003地球表面和大气微波辐射的详细观测数据

AMSR/ADEOS-II L1A Raw Observation Counts V003 简介 AMSR/ADEOS-II L1A Raw Observation Counts V003数据是由日本航空航天研究开发机构&#xff08;JAXA&#xff09;的AMSR (Advanced Microwave Scanning Radiometer)仪器收集的一组原始观测计数数据。这些数据是从ADEOS-I…

ARM阻击高通:AI PC大战与芯片之争

引言 在AI PC领域&#xff0c;高通的X Elite芯片因为其高性能和低功耗&#xff0c;一度被认为是未来的主导者。然而&#xff0c;ARM公司却通过法律手段试图阻止高通的独大&#xff0c;这不仅可能拖慢AI PC的发展进程&#xff0c;还引发了业界的广泛关注。本文将深入探讨ARM和高…

仙讯畅通无阻:探索MQ阵法的强大功能

MQ起源 IBM MQ&#xff1a;IBM MQ是IBM推出的一系列消息导向中间件产品&#xff0c;最初在1993年12月发布。它最初被称为MQSeries&#xff0c;2002年更名为WebSphere MQ&#xff0c;以加入WebSphere产品系列。2014年4月&#xff0c;它被重新命名为IBM MQ。Apache ActiveMQ&…

corepack管理包管理器;nvm管理node版本;nrm管理npm源地址

corepack corepack 管理"包管理器"&#xff0c;包括 yarn 和 pnpm。corepack 并不能管理 npm。 corepack 是 nodejs 提供的功能&#xff0c;安装 nodejs 时 corepack 就一起安装了。它还是实验性功能&#xff0c;默认是关闭的&#xff0c;具体介绍看官方文档。 注…

【TKGQA】关于时间知识图谱问答的一篇综述阅读

前言 时间知识图谱问答&#xff08;TKGQA&#xff09;是KBQA中一个关注时间问题的重要子任务。时间问题包含时间约束、需要时间标记的答案&#xff0c;反映了现实世界事件的动态和演变性质。 一、TKGQA 1.1 概述 时间知识图谱&#xff08;TKG&#xff09;&#xff1a; 通常表…

Windows环境利用 OpenCV 中 CascadeClassifier 分类器识别人脸 c++

Windows环境中配置OpenCV 关于在Windows环境中配置opencv的说明&#xff0c;具体可以参考&#xff1a;VS2022 配置OpenCV开发环境详细教程。 CascadeClassifier 分类器 CascadeClassifier 是 OpenCV 库中的一个类&#xff0c;它用于实现一种快速的物体检测算法&#xff0c;称…

链动3+1商业模式:革新之路,引领企业持续增长

随着信息技术的飞速发展和互联网的不断渗透&#xff0c;企业的商业模式变革已经成为市场竞争中不可或缺的一环。在这个变革的浪潮中&#xff0c;链动31商业模式以其独特的逻辑和高效的激励机制&#xff0c;成为了业界瞩目的焦点。本文将深入探讨链动31模式的运作机制&#xff0…

基于STM32的智能停车场管理系统

目录 引言环境准备智能停车场管理系统基础代码实现&#xff1a;实现智能停车场管理系统 4.1 车位检测模块4.2 数据处理与分析4.3 控制系统实现4.4 用户界面与数据可视化应用场景&#xff1a;智能停车场管理与优化问题解决方案与优化收尾与总结 1. 引言 智能停车场管理系统通…

数据结构经典面试之列表——C#和C++篇

文章目录 1. 数据结构概述2. 列表&#xff08;List&#xff09;的基本概念与操作3. 列表的具体实现方式3.1 数组实现3.2 链表实现 4. 列表在C#和C中的使用示例4.1 C#中的列表使用示例4.2 C中的列表使用示例 5. 总结 数据结构是计算机科学中非常重要的一个领域&#xff0c;它主要…