苍穹外卖项目笔记(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 insert
    OperationType 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的条件下才会创建这个bean
    public 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.Driver
    host: localhost
    port: 3306
    database: sky_take_out
    username: root
    password: root

  alioss:
    endpoint: https://oss-cn-hangzhou.aliyuncs.com
    access-key-id: LTAI5tLmKyefze8CGYs47HQt
    access-key-secret: MvO3k0AIAm4GSNujs0fLug4Chyu4sB
    bucket-name: cq-takeout-test

 application.yml

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

  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}

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

上传文件的通用控制类 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 {
    @Autowired
    private 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 {

    @Autowired
    private DishMapper dishMapper;

    private DishFlavorMapper dishFlavorMapper;

    /**
     * 新增菜品和口味
     * @param dishDTO
     */
    @Transactional
    public 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
     */
    //加上事务注解,保证事务的一致性
    @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);
        }
    }

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

        //将查询到的数据封装到VO
        DishVO 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);
        }
    }

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

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

相关文章

QT搭建的Ros/librviz的GUI软件

1.前言 开发初期学习了下面博主的文章&#xff0c;也报了他在古月局的课&#xff0c;相当于感谢吧。 ROS Qt5 librviz人机交互界面开发一&#xff08;配置QT环境&#xff09;-CSDN博客​​​​​​​r 软件前期也是参考他的开源项目 GitHub - chengyangkj/Ros_Qt5_Gui_App …

如何实现数据通过表格批量导入数据库

文章目录 1. 准备工作2. 创建数据库表3. 编写导入脚本4. 优化和拓展4.1 批量插入的优势4.2 错误处理4.3 数据验证4.4 数据转换 5. 总结 &#x1f389;如何实现数据通过表格批量导入数据库 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&…

常见的8个JMeter压测问题

为什么在JMeter中执行压力测试时&#xff0c;出现连接异常或连接重置错误&#xff1f; 答案&#xff1a;连接异常或连接重置错误通常是由于服务器在处理请求时出现问题引起的。这可能是由于服务器过载、网络故障或配置错误等原因导致的。 解决方法&#xff1a; 确定服务器的…

OSG文字-osgText3D(5)

osgText3D 三维立体文字比二维平面文字显示效果更好&#xff0c;相对二维平面文字&#xff0c;它有非常好的立体显示效果。 在实际虚拟现实项目中&#xff0c;过多使用三维立体文字会降低染效率&#xff0c;加重渲染负担&#xff0c;相对平面二维文字&#xff0c;它占用的内存是…

Linux C IO复用

IO复用 概述IO模型阻塞式IO非阻塞式IOIO复用select、poll、epoll异同 信号驱动式IO异步IO select函数select示例代码 poll函数poll示例代码 epoll函数创建  epoll_create注册、修改、删除  epoll_ctl轮询 I/O 事件的发生  epoll_waitepoll示例代码 基于TCP和epoll在线多人…

APP自动化之Poco框架

今天给大家介绍一款自动化测试框架Poco&#xff0c;其脚本写法非常简洁、高效&#xff0c;其元素定位器效率更快&#xff0c;其本质基于python的第三方库&#xff0c;调试起来也会非常方便&#xff0c;能够很好的提升自动化测试效率&#xff0c;节省时间。 (一&#xff09;背景…

DDD神药:去哪儿结合DDD,实现架构大调优

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 谈谈你的DDD落地经验&#xff1f; 谈谈你对DDD的理解&#x…

浅谈低压绝缘监测及定位系统在海上石油平台的研究与应用

安科瑞 华楠 摘要&#xff1a;海上石油平台低压系统与陆地电力系统有很大区别&#xff0c;其属于中性点绝缘系统&#xff0c;在出现单相接地故障时&#xff0c;系统允许带故障正常运行2 h&#xff0c;保证海上重要电气设备不会立即关停。现以渤海某海上平台为例&#xff0c;其…

Leetcode—13.罗马数字转整数【简单】

2023每日刷题&#xff08;三十七&#xff09; Leetcode—13.罗马数字转整数 算法思想 当前位置的元素比下个位置的元素小&#xff0c;就减去当前值&#xff0c;否则加上当前值 实现代码 int getValue(char c) {switch(c) {case I:return 1;case V:return 5;case X:return 1…

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

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

Python安装入门

目录 1 从应用商店安装2 通过官方安装3 验证安装是否成功4 打印hello world总结 1 从应用商店安装 推荐使用微软的应用商店安装&#xff0c;打开开始菜单 在应用商店搜索python 选择最新的版本下载并安装即可 2 通过官方安装 也可以使用官网的安装包&#xff0c;输入如下网…

【数据结构初阶】栈和队列

栈和队列 1.栈1.1栈的概念和结构1.2栈的实现 2.队列2.1队列的概念和结构2.2队列的实现 1.栈 1.1栈的概念和结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。…

逻辑漏洞(业务逻辑)dami CMS

逻辑漏洞&#xff08;业务支付逻辑漏洞&#xff09;dami CMS 0x01 业务逻辑简介 业务逻辑指的是一个系统或应用程序中的实际业务规则和流程。它描述了如何处理特定的业务需求、数据和操作。业务逻辑通常是根据特定行业或组织的需求而设计的。 在软件开发中&#xff0c;业务逻…

2021年06月 Scratch(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 小明同学设计了一款游戏,其中一段程序如下图所示,下面这段程序可以实现哪项功能? A:在任何地方点击鼠标,角色都会移到鼠标位置 B:没有任何操作的时候角色会在舞台区域随机移动…

接口自动化测试实战经验分享,测试用例也能自动生成

作为测试&#xff0c;你可能会对以下场景感到似曾相识&#xff1a;开发改好的 BUG 反复横跳&#xff1b;版本兼容逻辑多&#xff0c;修复一个 BUG 触发了更多 BUG&#xff1b;上线时系统监控毫无异常&#xff0c;过段时间用户投诉某个页面无数据&#xff1b;改动祖传代码时如履…

C#学习相关系列之Linq用法---group和join相关用法(三)

一、Group用法 在C#的LINQ中&#xff0c;Grou将集合中的元素按照指定的键进行分组。Group方法返回一个IEnumerable<IGrouping<TKey, TElement>>类型的集合&#xff0c;其中TKey表示分组的键类型&#xff0c;TElement表示集合中元素的类型。每个IGrouping<TKey, …

机器学习实战第1天:鸢尾花分类任务

专栏介绍 欢迎订阅专栏——机器学习实战 机器学习实战_Nowl的博客-CSDN博客 纸上得来终觉浅 本专栏项目将着重于解决各类实际机器学习问题&#xff0c;带你上手各种场景的实际问题 数据集可以在我的资源中找到&#xff0c;也可以自行搜索 文中导入数据集的路径要改成自己的…

华为obs上传下载-Java版 2023-11-23

弄了半天&#xff0c;老师帮弄成功了&#xff0c;经过同意&#xff0c;分享到网上&#xff0c;希望能帮助更多人&#xff0c;至于怎么弄的&#xff0c;我也不知道。 创建idea项目后&#xff0c;项目结构&#xff0c;对应文件没有的创一个 pom.xm 注意改Java版本&#xff0c;我…

解决Vision Transformer在任意尺寸图像上微调的问题:使用timm库

解决Vision Transformer在任意尺寸图像上微调的问题&#xff1a;使用timm库 文章目录 一、ViT的微调问题的本质二、Positional Embedding如何处理1&#xff0c;绝对位置编码2&#xff0c;相对位置编码3&#xff0c;对位置编码进行插值 三、Patch Embedding Layer如何处理四、使…

算法设计与分析复习--分支界限法

文章目录 上一篇分支界限法性质装载问题0-1背包问题单源最短路问题最大团问题下一篇 上一篇 算法设计与分析复习–回溯法&#xff08;二&#xff09; 分支界限法性质 分支界限法是按广度优先策略或最小耗费优先遍历问题的解空间树。 搜索解空间&#xff1a; 子集树排列树 …