物流项目第五期(运费计算实现、责任链设计模式运用)

news2025/5/23 22:49:46

前四期:

物流项目第一期(登录业务)-CSDN博客

物流项目第二期(用户端登录与双token三验证)-CSDN博客

物流项目第三期(统一网关、工厂模式运用)-CSDN博客

 物流项目第四期(运费模板列表实现)-CSDN博客

运费计算

运费计算的实现基本是三个步骤:

第一步,根据收件人、发件人的地址,查找对应的模板

第二步,计算实际计费的重量(使用轻抛系数将体积转化为重量,与实际重量相比,取大值)

第三步,按照首重 + 续重的方式计算出总价

基本的流程如下:

功能实现

    /**
     * 运费计算
     *
     * @param waybillDTO 运费计算对象
     * @return 运费模板对象,不仅包含模板数据还包含:computeWeight、expense 字段
     */
    CarriageDTO compute(WaybillDTO waybillDTO);

    /**
     * 根据模板类型查询模板,经济区互寄不通过该方法查询模板
     *
     * @param templateType 模板类型:1-同城寄,2-省内寄,4-跨省
     * @return 运费模板
     */
    CarriageEntity findByTemplateType(Integer templateType);
/**
 * 根据运单信息计算运费(主方法)
 *
 * @param waybillDTO 运单数据对象,包含发件城市、收件城市、重量、体积等信息
 * @return 返回包含运费结果的 CarriageDTO 对象
 */
@Override
public CarriageDTO compute(WaybillDTO waybillDTO) {
    // 1. 根据传入的运单信息查找匹配的运费模板(根据同城、省内、经济区、跨省等规则判断)
    CarriageEntity carriage = this.findCarriage(waybillDTO);

    // 2. 计算实际计费重量:
    // - 如果有体积,则按体积换算成“体积重量”;
    // - 否则取实际重量;
    // - 取两者最大值作为最终计费重量;
    double computeWeight = this.getComputeWeight(waybillDTO, carriage);

    // 3. 开始计算运费:
    // 公式:首重费用 + (计费重量 - 1kg) × 续重单价
    double expense = carriage.getFirstWeight() + ((computeWeight - 1) * carriage.getContinuousWeight());

    // 使用 NumberUtil.round 方法保留一位小数(四舍五入)
    expense = NumberUtil.round(expense, 1).doubleValue();

    // 4. 构造返回结果对象:
    // 将数据库实体对象转换为 DTO,并设置计算出的运费和计费重量
    CarriageDTO carriageDTO = CarriageUtils.toDTO(carriage);
    carriageDTO.setExpense(expense);         // 设置运费金额
    carriageDTO.setComputeWeight(computeWeight); // 设置实际计费重量

    // 5. 返回封装好的 DTO 结果
    return carriageDTO;
}
/**
 * 查找适用的运费模板
 *
 * @param waybillDTO 运单信息,用于判断是否同城、同省、经济区互寄等
 * @return 匹配的运费模板实体对象
 */
private CarriageEntity findCarriage(WaybillDTO waybillDTO) {

    // 1. 判断是否是“同城”快递:
    // 比较收件城市 ID 和发件城市 ID 是否相同
    if (ObjectUtil.equals(waybillDTO.getReceiverCityId(), waybillDTO.getSenderCityId())) {
        // 同城模板类型常量:CarriageConstant.SAME_CITY
        CarriageEntity carriageEntity = this.findByTemplateType(CarriageConstant.SAME_CITY);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity; // 找到就直接返回
        }
    }

    // 2. 判断是否是“同省”快递:
    // 获取收件人所在城市的父级行政区划(省份ID)
    Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
    // 获取寄件人所在城市的父级行政区划(省份ID)
    Long senderProvinceId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();

    // 如果收发省份一致,说明是省内快递
    if (ObjectUtil.equal(receiverProvinceId, senderProvinceId)) {
        // 查询同省模板
        CarriageEntity carriageEntity = this.findByTemplateType(CarriageConstant.SAME_PROVINCE);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity; // 找到就返回
        }
    }

    // 3. 判断是否属于“经济区互寄”:
    // 获取所有经济区枚举配置(比如华东、华南、华北等)
    LinkedHashMap<String, EconomicRegionEnum> EconomicRegionMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
    EconomicRegionEnum economicRegionEnum = null;

    // 遍历每个经济区,检查当前收发省份是否都属于该区域
    for (EconomicRegionEnum regionEnum : EconomicRegionMap.values()) {
        boolean result = ArrayUtil.containsAll(regionEnum.getValue(), receiverProvinceId, senderProvinceId);
        if (result) {
            economicRegionEnum = regionEnum; // 找到匹配的经济区
            break;
        }
    }

    if (ObjectUtil.isNotEmpty(economicRegionEnum)) {
        // 构建查询条件:
        // 模板类型为经济区(CarriageConstant.ECONOMIC_ZONE)
        // 快递类型为常规速递(CarriageConstant.REGULAR_FAST)
        // 关联城市字段中包含经济区编码
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers
                .lambdaQuery(CarriageEntity.class)
                .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
                .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
                .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());

        // 查询模板
        CarriageEntity carriageEntity = super.getOne(queryWrapper);
        if (ObjectUtil.isNotEmpty(carriageEntity)) {
            return carriageEntity; // 找到就返回
        }
    }

    // 4. 最终兜底策略:跨省快递
    return this.findByTemplateType(CarriageConstant.TRANS_PROVINCE);
}
/**
 * 根据体积参数与实际重量计算最终的计费重量
 *
 * @param waybillDTO 运单信息(含重量、长宽高等)
 * @param carriage   运费模板(含轻抛系数)
 * @return 返回最终计费重量(double 类型)
 */
private double getComputeWeight(WaybillDTO waybillDTO, CarriageEntity carriage) {

    // 1. 获取体积参数:
    Integer volume = waybillDTO.getVolume(); // 用户可能已经传了体积

    if (ObjectUtil.isEmpty(volume)) {
        try {
            // 如果没有传体积,则根据长宽高计算体积(单位:立方厘米)
            volume = waybillDTO.getMeasureLong() * waybillDTO.getMeasureWidth() * waybillDTO.getMeasureHigh();
        } catch (Exception e) {
            // 出错时设为0,防止异常中断
            volume = 0;
        }
    }

    // 2. 计算体积重量(用于轻泡货):
    // 体积 ÷ 轻抛系数 → 得到体积重量(保留一位小数)
    BigDecimal volumeWeight = NumberUtil.div(volume, carriage.getLightThrowingCoefficient(), 1);

    // 3. 获取实际重量(可能带小数),并保留一位小数
    double realWeight = NumberUtil.round(waybillDTO.getWeight(), 1).doubleValue();

    // 4. 取体积重量与实际重量中的较大者作为基础计费重量
    double computeWeight = NumberUtil.max(volumeWeight.doubleValue(), realWeight);

    // 5. 根据不同区间,对计费重量进行“续重规则”处理:

    // 规则一:≤1kg 的,按 1kg 计费
    if (computeWeight <= 1) {
        return 1;
    }

    // 规则二:1kg ~ 10kg 的,保留原始数值(精确到 0.1kg)
    if (computeWeight <= 10) {
        return computeWeight;
    }

    // 规则三:≥100kg 的,四舍五入取整数
    if (computeWeight >= 100) {
        return NumberUtil.round(computeWeight, 0).doubleValue();
    }

    // 规则四:10kg ~ 100kg 的,以 0.5kg 为一个计价单位
    int integer = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).intValue(); // 取整数部分
    double decimalPart = NumberUtil.sub(computeWeight, integer); // 小数部分

    if (decimalPart == 0) {
        return integer; // 整数,直接返回
    }

    if (decimalPart <= 0.5) {
        return NumberUtil.add(integer, 0.5); // 0.5以内加0.5
    }

    return NumberUtil.add(integer, 1); // 超过0.5,进位
}
/**
 * 根据模板类型查询运费模板
 *
 * @param templateType 模板类型(如:同城、省内、跨省)
 * @return 返回匹配的运费模板实体对象
 */
@Override
public CarriageEntity findByTemplateType(Integer templateType) {
    // 如果调用的是经济区类型的模板,抛出异常(因为 findCarriage 方法已单独处理经济区情况)
    if (ObjectUtil.equals(templateType, CarriageConstant.ECONOMIC_ZONE)) {
        throw new SLException(CarriageExceptionEnum.METHOD_CALL_ERROR);
    }

    // 构建查询条件:
    // 模板类型 = templateType
    // 快递类型 = 常规速递(REGULAR_FAST)
    LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers
            .lambdaQuery(CarriageEntity.class)
            .eq(CarriageEntity::getTemplateType, templateType)
            .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST);

    // 查询唯一一条记录并返回
    return super.getOne(queryWrapper);
}
    @PostMapping("compute")
    @ApiOperation(value = "运费计算")
    public CarriageDTO compute(@RequestBody WaybillDTO waybillDTO) {
        return carriageService.compute(waybillDTO);
    }

代码优化

在上述的运费计算的代码中,通过条件查找运费模板的方法中,判断了很多种情况,如果后续要再增加不同类型的模板或者调整模板之间的优先级,就必须改动代码,所以这样的实现扩展性并不好,也不够灵活。这里可以通过【责任链设计模式】来优化。

解释:

责任链模式是一种行为模式,把多个处理器组成一条链,但具体由哪个处理器来处理,根据条件判断来确定,如果不能处理会传递给该链中的下一个处理器,直到有处理器处理它为止。

之所以采用【责任链】模式,是因为在查找模板时,不同的模板处理逻辑不同,并且这些逻辑组成了一条处理链,有开头有结尾,只要能找到符合条件的模板即结束。

 定义处理链抽象类

package com.sl.ms.carriage.handler;

import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.entity.CarriageEntity;

/**
 * 运费模板处理链的抽象定义
 *
 * 该抽象类定义了一个运费模板处理链的基本结构,允许通过链式调用来查找适用的运费模板。
 * 每个具体的处理器(Handler)需要继承此类并实现 doHandler 方法。
 */
public abstract class AbstractCarriageChainHandler {

    /**
     * 下一个处理器对象,用于形成处理链。
     * 如果当前处理器无法找到合适的运费模板,则将请求传递给下一个处理器。
     */
    private AbstractCarriageChainHandler nextHandler;

    /**
     * 抽象方法:执行过滤方法,根据输入参数查找运费模板。
     *
     * @param waybillDTO 输入参数,包含运单的相关信息(如发件城市、收件城市等)
     * @return 返回匹配的运费模板实体对象,如果没有找到则返回 null
     */
    public abstract CarriageEntity doHandler(WaybillDTO waybillDTO);

    /**
     * 执行下一个处理器的方法。
     *
     * 当前处理器未能找到运费模板时,可以调用此方法将请求传递给下一个处理器。
     * 如果下游处理器为空或当前处理器已经找到了运费模板,则直接返回当前结果。
     *
     * @param waybillDTO     输入参数,包含运单的相关信息
     * @param carriageEntity 上一个处理器处理得到的运费模板对象,如果未找到则为 null
     * @return 返回下一个处理器处理后的结果,或者直接返回当前的 carriageEntity(如果已找到)
     */
    protected CarriageEntity doNextHandler(WaybillDTO waybillDTO, CarriageEntity carriageEntity) {
        // 如果没有设置下一个处理器 或者 当前处理器已经找到了运费模板,则直接返回当前结果
        if (nextHandler == null || carriageEntity != null) {
            return carriageEntity;
        }
        // 否则继续调用下一个处理器进行处理
        return nextHandler.doHandler(waybillDTO);
    }

    /**
     * 设置下一个处理器。
     *
     * 通过此方法可以构建处理链,每个处理器可以指定它的下一个处理器,从而形成一条完整的处理链。
     *
     * @param nextHandler 下游处理器对象
     */
    public void setNextHandler(AbstractCarriageChainHandler nextHandler) {
        this.nextHandler = nextHandler;
    }
}

同城寄

/**
 * 同城寄
 */
@Order(100) //定义顺序
@Component
public class SameCityChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = null;
        if (ObjectUtil.equals(waybillDTO.getReceiverCityId(), waybillDTO.getSenderCityId())) {
            //同城
            carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.SAME_CITY);
        }
        return doNextHandler(waybillDTO, carriageEntity);
    }
}

省内寄

/**
 * 省内寄
 */
@Order(200) //定义顺序
@Component
public class SameProvinceChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;
    @Resource
    private AreaFeign areaFeign;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = null;
        // 获取收寄件地址省份id
        Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
        Long senderProvinceId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();
        if (ObjectUtil.equal(receiverProvinceId, senderProvinceId)) {
            //省内
            carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.SAME_PROVINCE);
        }
        return doNextHandler(waybillDTO, carriageEntity);
    }
}

经济区互寄

/**
 * 经济区互寄
 */
@Order(300) //定义顺序
@Component
public class EconomicZoneChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;
    @Resource
    private AreaFeign areaFeign;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = null;

        // 获取收寄件地址省份id
        Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
        Long senderProvinceId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();

        //获取经济区城市配置枚举
        LinkedHashMap<String, EconomicRegionEnum> EconomicRegionMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        EconomicRegionEnum economicRegionEnum = null;
        for (EconomicRegionEnum regionEnum : EconomicRegionMap.values()) {
            //该经济区是否全部包含收发件省id
            boolean result = ArrayUtil.containsAll(regionEnum.getValue(), receiverProvinceId, senderProvinceId);
            if (result) {
                economicRegionEnum = regionEnum;
                break;
            }
        }

        if (ObjectUtil.isNotEmpty(economicRegionEnum)) {
            //根据类型编码查询
            LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(CarriageEntity.class)
                    .eq(CarriageEntity::getTemplateType, CarriageConstant.ECONOMIC_ZONE)
                    .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
                    .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());
            carriageEntity = this.carriageService.getOne(queryWrapper);
        }

        return doNextHandler(waybillDTO, carriageEntity);
    }
}

跨省寄

/**
 * 跨省
 */
@Order(400) //定义顺序
@Component
public class TransProvinceChainHandler extends AbstractCarriageChainHandler {

    @Resource
    private CarriageService carriageService;

    @Override
    public CarriageEntity doHandler(WaybillDTO waybillDTO) {
        CarriageEntity carriageEntity = this.carriageService.findByTemplateType(CarriageConstant.TRANS_PROVINCE);
        return doNextHandler(waybillDTO, carriageEntity);
    }
}

组装处理链

/**
 * 查找运费模板处理链 @Order注解指定handler顺序
 *
 * 该类用于组装和管理一系列的运费模板处理器(AbstractCarriageChainHandler),通过Spring的依赖注入机制,
 * 按照@Order注解指定的顺序自动注入到List中,并构建处理链。
 */
@Component
public class CarriageChainHandler {

    /**
     * Spring注入的处理器列表,按照@Order注解从小到大排序。
     * 
     * 利用Spring的@Resource注解自动注入实现了AbstractCarriageChainHandler接口的所有bean实例,
     * 并按照@Order注解指定的顺序进行排序。
     */
    @Resource
    private List<AbstractCarriageChainHandler> chainHandlers;

    /**
     * 处理链的第一个处理器。
     * 
     * 在构造处理链时设置,指向处理链中的第一个处理器对象。
     */
    private AbstractCarriageChainHandler firstHandler;

    /**
     * 组装处理链。
     * 
     * 使用@PostConstruct注解标记的方法,在Spring容器初始化完成后自动调用。
     * 此方法负责将各个处理器按顺序连接起来,形成一条完整的处理链。
     */
    @PostConstruct
    private void constructChain() {
        // 检查chainHandlers是否为空,如果为空则抛出异常提示未找到处理器
        if (CollUtil.isEmpty(chainHandlers)) {
            throw new SLException("not found carriage chain handler!");
        }

        // 设置处理链的第一个节点为chainHandlers列表中的第一个元素
        firstHandler = chainHandlers.get(0);

        // 遍历chainHandlers列表,依次设置每个处理器的下一个处理器
        for (int i = 0; i < chainHandlers.size(); i++) {
            if (i == chainHandlers.size() - 1) {
                // 对于最后一个处理器,设置其下游处理器为null,表示没有后续处理器
                chainHandlers.get(i).setNextHandler(null);
            } else {
                // 对于非最后一个处理器,设置其下游处理器为下一个处理器
                chainHandlers.get(i).setNextHandler(chainHandlers.get(i + 1));
            }
        }
    }

    /**
     * 根据运单信息查找运费模板。
     * 
     * 从处理链的第一个处理器开始处理,逐级传递直到找到匹配的运费模板或遍历完所有处理器。
     *
     * @param waybillDTO 运单数据传输对象,包含发件城市、收件城市等信息
     * @return 返回匹配的运费模板实体对象
     */
    public CarriageEntity findCarriage(WaybillDTO waybillDTO) {
        // 从处理链的第一个处理器开始处理
        return firstHandler.doHandler(waybillDTO);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

X 下载器 2.1.42 | 国外媒体下载工具 网页视频嗅探下载

X 下载器让你能够轻松地从社交应用如Facebook、Instagram、TikTok等下载视频和图片。通过内置浏览器访问网站&#xff0c;它能自动检测视频和图片&#xff0c;只需点击下载按钮即可完成下载。去除广告&#xff0c;解锁本地会员&#xff0c;享受无广告打扰的下载体验。 大小&am…

STM32 CAN CANAerospace

STM32的CAN模块对接CANAerospace 刚开始报错如下. 设备开机后整个CAN消息就不发了. USB_CAN调试器报错如下. index time Name ID Type Format Len Data00000001 000.000.000 Event 总线错误 DATA STANDARD 8 接收过程错误-格…

完整改进RIME算法,基于修正多项式微分学习算子Rime-ice增长优化器,完整MATLAB代码获取

1 简介 为了有效地利用雾状冰生长的物理现象&#xff0c;最近开发了一种优化算法——雾状优化算法&#xff08;RIME&#xff09;。它模拟硬雾状和软雾状过程&#xff0c;构建硬雾状穿刺和软雾状搜索机制。在本研究中&#xff0c;引入了一种增强版本&#xff0c;称为修改的RIME…

服务器安装xfce桌面环境并通过浏览器操控

最近需要运行某个浏览器的脚本&#xff0c;但是服务器没有桌面环境&#xff0c;无法使用&#xff0c;遂找到了KasmVNC&#xff0c;并配合xfce实现低占用的桌面环境&#xff0c;可以直接使用浏览器进行操作 本文基于雨云——新一代云服务提供商的Debian11服务器操作&#xff0c;…

Oracle 创建外部表

找别人要一下数据&#xff0c;但是他发来一个 xxx.csv 文件&#xff0c;怎么办&#xff1f; 1、使用视图化工具导入 使用导入工具导入&#xff0c;如 DBeaver&#xff0c;右击要导入的表&#xff0c;选择导入数据。 选择对应的 csv 文件&#xff0c;下一步就行了&#xff08;如…

大语言模型 17 - MCP Model Context Protocol 介绍对比分析 基本环境配置

MCP 基本介绍 官方地址&#xff1a; https://modelcontextprotocol.io/introduction “MCP 是一种开放协议&#xff0c;旨在标准化应用程序向大型语言模型&#xff08;LLM&#xff09;提供上下文的方式。可以把 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 提供了一种…

【软考向】Chapter 9 数据库技术基础

基本概念数据库的三级模式结构 数据模型E-R 模型关系模型各种键完整性约束 关系代数5 种基本的关系代数运算&#xff1a;并、差、笛卡儿积、投影和选择扩展的关系代数运算&#xff1a;交(Intersection)、连接(Join)、除(Division)、广义投影(Generalized Projection)、外连接(O…

实战:Dify智能体+Java=自动化运营工具!

我们在运营某个圈子的时候&#xff0c;可能每天都要将这个圈子的“热门新闻”发送到朋友圈或聊天群里&#xff0c;但依靠传统的实现手段非常耗时耗力&#xff0c;我们通常要先收集热门新闻&#xff0c;再组装要新闻内容&#xff0c;再根据内容设计海报等。 那怎么才能简化并高…

STM32单片机GUI系统1 GUI基本内容

目录 一、GUI简介 1、emWin 2、LVGL (Light and Versatile Graphics Library) 3、TouchGFX 4、Qt for Embedded 5、特性对比总结 二、LVGL移植要求 三、优化LVGL运行效果方法 四、LVGL系统文件 一、GUI简介 在嵌入式系统中&#xff0c;emWin、LVGL、TouchGFX 和 Qt 是…

应届本科生简历制作指南

一、找一个专业的简历模板 首先&#xff0c;你需要访问 Overleaf 的官方网站&#xff0c;也就是Overleaf, Online LaTeX Editor&#xff0c;进入页面后&#xff0c;点击注册按钮&#xff0c;按照提示填写相关信息来创建一个属于自己的账号&#xff0c;通常需要填写用户名、邮箱…

PyTorch可视化工具——使用Visdom进行深度学习可视化

文章目录 前置环境Visdom安装并启动VisdomVisdom图形APIVisdom静态更新API详解通用参数说明使用示例Visdom动态更新API详解1. 使用updateappend参数2. ~~使用vis.updateTrace方法~~3. 完整训练监控示例 Visdom可视化操作散点图plot.scatter()散点图案例线性图vis.line()vis.lin…

企业级爬虫进阶开发指南

企业级爬虫进阶开发指南 一、分布式任务调度系统的深度设计 1.1 架构设计原理 图表 1.2 核心代码实现与注释 分布式锁服务 # distributed_lock.py import redis import timeclass DistributedLock:def __init__(self, redis_conn):self.redis = redis_connself.lock_key = …

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…

数据结构实验10.1:内部排序的基本运算

文章目录 一&#xff0c;实验目的二&#xff0c;实验内容1. 数据生成与初始化2. 排序算法实现&#xff08;1&#xff09;直接插入排序&#xff08;2&#xff09;二分插入排序&#xff08;3&#xff09;希尔排序&#xff08;4&#xff09;冒泡排序&#xff08;5&#xff09;快速…

wps编辑技巧

1、编辑模式 2、图片提取方法&#xff1a;右键保存图片 可以直接右键保存下来看看是否是原始图&#xff0c;如果歪着的图&#xff0c;可能保存下来是正的&#xff0c;直接保存试下 3、加批注

开放世界RPG:无缝地图与动态任务的拓扑学架构

目录 开放世界RPG:无缝地图与动态任务的拓扑学架构引言第一章 地图分块系统1.1 动态加载算法1.2 内存管理模型第二章 任务拓扑网络2.1 任务依赖图2.2 动态可达性分析第三章 NPC行为系统3.1 行为森林架构3.2 日程规划算法第四章 动态事件系统4.1 事件传播模型4.2 玩家影响指标第…

【图像处理入门】1. 数字图像的本质:从像素到色彩模型

作为图像处理的开篇&#xff0c;本文将带你拆解数字图像的底层逻辑&#xff1a;从模拟图像到数字信号的神奇转换&#xff0c;到像素世界的微观构成&#xff0c;再到彩色图像的编码奥秘。通过 Python 代码实战&#xff0c;你将亲手触摸图像的 “基因”—— 像素值&#xff0c;并…

(已解决:基于WSL2技术)Windows11家庭中文版(win11家庭版)如何配置和使用Docker Desktop

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 解决方法&#xff1a; 1、使用WSL2技术&#xff08;亲测有效&#xff09; 注意&#xff1a; 2、开启Hyper-V功能&#xff08;未经亲测&#xff0c;待研究&#xff09; 问题现象&#xff1a; 今天想在本…

Ubuntu20.04部署KVM

文章目录 一. 环境准备关闭防火墙&#xff08;UFW&#xff09;禁用 SELinux更换镜像源检查 CPU 虚拟化支持 二. 安装KVM安装 KVM 及相关组件启动 libvirtd 服务验证安装创建虚拟机 一. 环境准备 4C8G&#xff0c;50G硬盘——VMware Workstation需要给虚拟机开启虚拟化引擎 roo…

OpenCV CUDA 模块图像过滤------创建一个高斯滤波器函数createGaussianFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::createGaussianFilter 是 OpenCV CUDA 模块中的一个工厂函数&#xff0c;用于创建一个高斯滤波器。这个滤波器可以用来平滑图像&#…