logistics/logistics_service.py

166 lines
5.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""物流费用计算统一入口服务"""
import re
from typing import Dict, List, Any, Optional
from utils.Package import Package, Package_group
from utils.countryOperator import OperateCountry
from logisticsClass.logisticsBaseClass import PortType
class LogisticsService:
"""物流费用计算统一服务"""
@staticmethod
def _detect_country(postcode: str) -> str:
"""根据邮编格式自动识别国家"""
postcode = postcode.strip().upper()
# 英国邮编格式:字母+数字(+字母)+空格+数字+字母
# 支持格式: SW1A 1AA, M1 1AA, BT1 1AA, AA11 1AA 等
if re.match(r'^[A-Z]{1,2}[0-9][A-Z0-9]?\s?[0-9][A-Z]{2}$', postcode):
return "UK"
# 美国邮编格式5位数字或5位-4位
if re.match(r'^\d{5}(-\d{4})?$', postcode):
return "US"
# 澳洲邮编格式4位数字
if re.match(r'^\d{4}$', postcode):
return "AU"
# 欧洲格式(德国、法国等)
if re.match(r'^\d{5}$', postcode):
return "DE" # 默认德国
raise ValueError(f"无法识别的邮编格式: {postcode}")
@staticmethod
def _parse_packages(packages_data: List[Dict]) -> Package_group:
"""解析包裹数据"""
packages = []
for i, pkg in enumerate(packages_data):
name = pkg.get("name", f"包裹{i+1}")
length = pkg.get("length", 0)
width = pkg.get("width", 0)
height = pkg.get("height", 0)
weight = pkg.get("weight", 0)
package = Package(name, length, width, height, weight)
packages.append(package)
return Package_group(packages)
@staticmethod
def calculate(postcode: str, packages_data: List[Dict],
port: PortType = PortType.DEFAULT) -> Dict[str, Any]:
"""
计算物流费用并返回最优渠道
Args:
postcode: 收件人邮编
packages_data: 包裹数据列表,格式为:
[
{"length": 63, "width": 59, "height": 48, "weight": 8000}, # 单位cm, g
...
]
port: 港口类型默认DEFAULT
Returns:
包含最优渠道和所有渠道费用的字典
"""
# 1. 识别国家
country = LogisticsService._detect_country(postcode)
# 2. 解析包裹
packages = LogisticsService._parse_packages(packages_data)
# 3. 创建国家操作对象
op_country = OperateCountry(country, port, packages, postcode)
# 4. 获取所有渠道费用
all_fees = op_country.get_all_tail_info()
# 5. 找出最优渠道
valid_fees = {k: v for k, v in all_fees.items() if v < 99999}
if not valid_fees:
return {
"country": country,
"postcode": postcode,
"optimal_channel": None,
"optimal_fee": None,
"currency": None,
"all_channels": all_fees,
"error": "所有渠道均不可用"
}
optimal_channel = min(valid_fees, key=valid_fees.get)
optimal_fee = valid_fees[optimal_channel]
currency = op_country.get_tail_currency(optimal_channel)
# 6. 构建结果
result = {
"country": country,
"postcode": postcode,
"optimal_channel": optimal_channel,
"optimal_fee": optimal_fee,
"currency": currency,
"all_channels": {},
"package_count": len(packages_data),
"total_weight": sum(p.weight for p in packages) / 1000, # kg
}
# 7. 添加所有渠道详情
for company, fee in all_fees.items():
company_type = op_country.get_logistic_type(company)
company_currency = op_country.get_tail_currency(company)
result["all_channels"][company] = {
"fee": fee if fee < 99999 else None,
"currency": company_currency,
"type": company_type,
"available": fee < 99999
}
return result
@staticmethod
def calculate_us(postcode: str, packages_data: List[Dict],
port: PortType = PortType.DEFAULT) -> Dict[str, Any]:
"""计算美国物流费用"""
return LogisticsService.calculate(postcode, packages_data, port)
@staticmethod
def calculate_uk(postcode: str, packages_data: List[Dict],
port: PortType = PortType.DEFAULT) -> Dict[str, Any]:
"""计算英国物流费用"""
return LogisticsService.calculate(postcode, packages_data, port)
@staticmethod
def calculate_au(postcode: str, packages_data: List[Dict],
port: PortType = PortType.DEFAULT) -> Dict[str, Any]:
"""计算澳洲物流费用"""
return LogisticsService.calculate(postcode, packages_data, port)
@staticmethod
def calculate_eur(postcode: str, packages_data: List[Dict],
port: PortType = PortType.DEFAULT) -> Dict[str, Any]:
"""计算欧洲物流费用"""
return LogisticsService.calculate(postcode, packages_data, port)
@staticmethod
def get_company_detail(postcode: str, packages_data: List[Dict],
company_name: str) -> Dict[str, Any]:
"""获取指定物流公司的费用明细"""
country = LogisticsService._detect_country(postcode)
packages = LogisticsService._parse_packages(packages_data)
op_country = OperateCountry(country, PortType.DEFAULT, packages, postcode)
detail = op_country.get_detail_amount(company_name, packages, postcode)
currency = op_country.get_tail_currency(company_name)
return {
"company": company_name,
"currency": currency,
"detail": detail,
"total": detail.get("tail_amount", 0)
}