资讯详情

搞定飞机托运行李价格计算,这3个实战项目细节救了我

📅 2026/9/22 13:28:40 | 华诺云谱 👁 阅读
搞定飞机托运行李价格计算,这3个实战项目细节救了我
搞定飞机托运行李价格计算,这3个实战项目细节救了我 很多兄弟都卡在同一个地方:语法背得滚瓜烂熟,LeetCode 算法题也能刷过,但真让你搭个完整的项目,脑子就一片空白。别急,这种“手残”不是你的问题,是缺乏实战项目的打磨。今天我们就拿一个看似简单但逻辑坑很多的业务场景——飞机托运行李价格计算,从零手撸一个完整的计算模块。 别小看这个需求,它涵盖了数据清洗、多规则判断、异常处理、单元测试以及性能优化,是一个极佳的入门级全栈练手题。我曾在掘金技术社区看到很多大厂面试真题里,类似的计费逻辑(如快递费、打车费)都是高频考点。咱们不整虚的,直接上代码,把坑填平。 项目目标与需求拆解 先搞清楚我们要干什么。航空公司托运行李的计费规则通常不是线性的,而是分段计价或者按重量档位收费。为了贴近真实场景,我们设定以下规则:免费额度:经济舱 20kg,公务舱 30kg,头等舱 40kg。 超重部分:超过免费额度后,每 1kg(不足 1kg 按 1kg 计)收取固定费用。不同舱位费率不同。 特殊件:如果行李包含易碎品或精密仪器,需额外支付保价费,且单件重量超过 30kg 时,费率翻倍。 输入校验:必须处理非法输入(如负数重量、非数字字符串)。我们的目标不是写一个 if-else 就完事,而是构建一个可扩展、可测试、高内聚的计算引擎。这能帮你从“写代码”进阶到“设计系统”。 目录结构设计 在动手写代码前,先规划好目录。好的结构能让后续维护变得轻松,这也是很多新手容易忽略的工程化细节。 luggage_calculator/ ├── __init__.py ├── config.py # 配置管理:舱位费率、免费额度 ├── exceptions.py # 自定义异常 ├── core/ │ ├── __init__.py │ ├── calculator.py # 核心计算逻辑 │ └── validator.py # 输入校验模块 ├── tests/ │ ├── __init__.py │ └── test_calculator.py # 单元测试 └── main.py # 入口文件/演示脚本这里采用了分层架构:config 存静态数据,core 存业务逻辑,tests 存测试用例。这种分离思想在任何中大型项目中都是通用的,养成习惯,受益终身。 核心代码实现 接下来是重头戏。我们将核心逻辑封装在 calculator.py 中,并引入策略模式的思想,虽然这里为了简洁用字典映射代替了复杂的策略类,但逻辑是一样的:将变化的规则与不变的逻辑分离。 1. 配置与异常定义 # config.py CABIN_RATES = {economy: {free_limit: 20, rate_per_kg: 15.0, premium_rate_multiplier: 2.0},business: {free_limit: 30, rate_per_kg: 25.0, premium_rate_multiplier: 2.0},first: {free_limit: 40, rate_per_kg: 40.0, premium_rate_multiplier: 2.0} }# exceptions.py class LuggageError(Exception):行李计算基础异常passclass InvalidWeightError(LuggageError):无效重量异常passclass UnknownCabinError(LuggageError):未知舱位异常pass2. 输入校验模块 很多 Bug 都源于对输入的不信任。在掘金技术社区的许多分享中,防御性编程被反复提及。我们单独写一个 validator.py。 # core/validator.py from exceptions import InvalidWeightError, UnknownCabinError from config import CABIN_RATESdef validate_input(weight: float, cabin_type: str, is_premium: bool = False):校验输入参数的合法性:param weight: 行李重量 (kg):param cabin_type: 舱位类型:param is_premium: 是否特殊件# 检查重量是否为数字if not isinstance(weight, (int, float)):raise InvalidWeightError(重量必须是数字类型)# 检查重量是否为正数if weight = 0:raise InvalidWeightError(重量必须大于0)# 检查舱位是否支持if cabin_type not in CABIN_RATES:raise UnknownCabinError(f不支持的舱位类型: {cabin_type})# 检查特殊件标志if not isinstance(is_premium, bool):raise ValueError(is_premium 必须是布尔值)3. 核心计算逻辑 这是整个模块的心脏。注意看注释,每一步都在处理边界情况。 # core/calculator.py from config import CABIN_RATES from core.validator import validate_input import mathclass LuggageCalculator:def __init__(self):self.config = CABIN_RATESdef calculate_fee(self, weight: float, cabin_type: str, is_premium: bool = False) - dict:计算托运行李费用:return: 包含详细费用的字典# 1. 前置校验validate_input(weight, cabin_type, is_premium)# 2. 获取当前舱位配置cabin_config = self.config[cabin_type]free_limit = cabin_config[free_limit]base_rate = cabin_config[rate_per_kg]# 3. 计算超重重量# 向上取整是关键:10.1kg 超重部分按 11kg 计算?不,是超出部分向上取整。# 假设免费额度内不收费,超出部分按 1kg 为单位向上取整excess_weight = max(0, weight - free_limit)billable_kg = math.ceil(excess_weight) # 向上取整# 4. 判断是否应用特殊费率# 规则:单件超过30kg 且 是特殊件,费率翻倍multiplier = 1.0if is_premium and weight 30:multiplier = cabin_config[premium_rate_multiplier]final_rate = base_rate * multiplier# 5. 计算总费用total_fee = billable_kg * final_rate# 6. 返回结构化数据,便于前端展示或日志记录return {total_fee: round(total_fee, 2),billable_kg: billable_kg,rate_per_kg: round(final_rate, 2),free_limit_used: min(weight, free_limit),cabin_type: cabin_type,is_premium_applied: bool(multiplier 1.0)}这段代码有几个亮点:math.ceil 的使用:模拟了现实世界中“不足 1kg 按 1kg 计”的规则。 结构化返回:没有直接返回一个浮点数,而是返回一个包含明细的字典。这在调试和对账时极其重要。 逻辑解耦:校验、配置获取、计算逻辑清晰分开。运行与测试 写代码不写测试,等于没写。我们用 pytest 来编写单元测试,确保我们的逻辑在各种边界情况下都是正确的。 # tests/test_calculator.py import pytest from core.calculator import LuggageCalculator from exceptions import InvalidWeightError, UnknownCabinError@pytest.fixture def calculator():return LuggageCalculator()def test_economy_within_limit(calculator):# 经济舱 15kg,未超重,费用应为 0result = calculator.calculate_fee(15, economy)assert result[total_fee] == 0assert result[billable_kg] == 0def test_economy_over_limit(calculator):# 经济舱 25kg,超重 5kg,费率 15元/kgresult = calculator.calculate_fee(25, economy)assert result[total_fee] == 75.0assert result[billable_kg] == 5def test_premium_heavy_item(calculator):# 公务舱 35kg,特殊件。免费30kg,超重5kg。# 基础费率25元,因为30kg且特殊件,费率翻倍为50元。result = calculator.calculate_fee(35, business, is_premium=True)assert result[total_fee] == 250.0assert result[rate_per_kg] == 50.0def test_invalid_weight(calculator):with pytest.raises(InvalidWeightError):calculator.calculate_fee(-10, economy)def test_unknown_cabin(calculator):with pytest.raises(UnknownCabinError):calculator.calculate_fee(10, vip)运行测试命令: pytest tests/ -v如果看到所有测试通过,恭喜你,核心逻辑已经跑通。这个环节的价值在于,当你后续修改费率逻辑时,测试会立刻告诉你哪里改错了,而不是等上线后用户投诉才发现问题。 优化扩展与避坑指南 代码跑通了,但这只是开始。在实际生产环境中,还有几个坑需要你提前踩平:并发与线程安全: 如果这是一个 Web 服务的一部分,LuggageCalculator 实例如果被多个线程共享,目前的代码是安全的,因为它没有修改实例状态(无状态计算)。但如果你引入了缓存(比如缓存计算结果以提升性能),就必须加锁或使用线程本地存储。费率动态化: 目前费率是硬编码在 config.py 里的。在真实业务中,费率可能会随季节、航线变化。建议将配置从代码中剥离,放入数据库或 Redis,并设计一个配置加载器,支持热更新。日志记录: 在 calculate_fee 方法中加入 logging 模块,记录每次计算的输入、输出和耗时。当用户投诉“为什么我要付这么多钱”时,你可以通过日志还原当时的计算过程,这是排查问题的救命稻草。国际化考虑: 货币单位、重量单位(磅 vs 公斤)都需要抽象出来。不要硬编码 15.0,而应该定义一个 Currency 和 Unit 枚举,方便后续扩展多币种和多单位支持。API 封装: 如果用 Flask 或 FastAPI 封装,记得对返回的 JSON 数据进行序列化优化,避免暴露内部配置细节。小结 通过这个飞机托运行李价格计算的实战项目,我们不仅解决了一个具体的业务问题,更锻炼了工程化的思维。从目录规划、异常处理、单元测试到性能优化,每一步都是工业级开发的缩影。 很多开发者觉得业务逻辑简单,不屑于深入,但正是这些看似简单的 CRUD 和计算逻辑,构成了系统的基石。把基础打牢,比盲目追逐新技术框架更重要。 在掘金技术社区的讨论区,经常有朋友问:在涉及金额计算时,是用 float 还是 Decimal?这是一个经典争议。我目前的做法是在前端展示时用 float,在后端核心计费时用 Python 的 decimal 模块,以避免二进制浮点数精度丢失问题。 你公司项目里是怎么处理这类计费逻辑的?有没有遇到过精度丢失或者规则冲突的坑?欢迎在评论区分享你的经验,我们一起避坑。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。