czlexpress-for-woocommerce/includes/class-czl-rate-calculator.php
wood chen 16965b29e1 Enhance CZL Express for WooCommerce Plugin Functionality
- Updated plugin URI for better branding consistency.
- Added environment checks to ensure WooCommerce is installed and meets version requirements.
- Improved AJAX handling for shipment creation and tracking updates, including enhanced error messages.
- Streamlined order management with new custom order statuses and improved logging for better tracking.
- Removed deprecated API test page and updated admin interface for clarity.
- Enhanced localization by ensuring all translatable strings use esc_html functions for security.

These changes improve the robustness, usability, and maintainability of the CZL Express plugin.
2025-01-15 00:08:34 +08:00

382 lines
15 KiB
PHP
Raw 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.

<?php
class CZL_Rate_Calculator {
private $api;
private $product_groups;
private $exchange_rate;
public function __construct() {
$this->api = new CZL_API();
$this->product_groups = $this->get_product_groups();
$this->exchange_rate = $this->get_exchange_rate();
}
/**
* 获取产品分组配置
*/
private function get_product_groups() {
// 获取产品分组配置
$groups = get_option('czl_product_groups', array());
// 如果没有配置,使用默认分组
if (empty($groups)) {
$groups = array(
'ups_expedited' => array(
'enabled' => true,
'groupName' => 'UPS Expedited',
'prefixes' => array('UPS 蓝单')
),
'ups_saver' => array(
'enabled' => true,
'groupName' => 'UPS Saver',
'prefixes' => array('UPS 红单')
),
'fedex_ie' => array(
'enabled' => true,
'groupName' => 'FEDEX IE',
'prefixes' => array('FEDEX IE')
),
'fedex_ip' => array(
'enabled' => true,
'groupName' => 'FEDEX IP',
'prefixes' => array('FEDEX IP')
),
'dhl' => array(
'enabled' => true,
'groupName' => 'DHL',
'prefixes' => array('DHL')
),
'europe_normal' => array(
'enabled' => true,
'groupName' => __('Customs duty line', 'czlexpress-for-woocommerce'),
'prefixes' => array(
__('欧美经济专线(普货)', 'czlexpress-for-woocommerce'),
__('欧洲经济专线(普货)', 'czlexpress-for-woocommerce')
)
),
'europe_fast' => array(
'enabled' => true,
'groupName' => __('Fast customs duty line', 'czlexpress-for-woocommerce'),
'prefixes' => array(
__('欧美标准专线(普货)', 'czlexpress-for-woocommerce'),
__('欧洲标准专线(普货)', 'czlexpress-for-woocommerce')
)
),
'ems' => array(
'enabled' => true,
'groupName' => __('EMS', 'czlexpress-for-woocommerce'),
'prefixes' => array(__('EMS', 'czlexpress-for-woocommerce'))
),
'czl_uae' => array(
'enabled' => true,
'groupName' => 'CZL UAE Line',
'prefixes' => array('CZL阿联酋经济专线')
),
'czl_uae_fast' => array(
'enabled' => true,
'groupName' => 'CZL UAE Fast Line',
'prefixes' => array('CZL阿联酋特快专线')
)
);
update_option('czl_product_groups', $groups);
}
// 只返回启用的分组
return array_filter($groups, function($group) {
return !empty($group['enabled']);
});
}
/**
* 获取汇率
*/
private function get_exchange_rate() {
// 获取WooCommerce货币设置
$wc_currency = get_woocommerce_currency();
if ($wc_currency === 'CNY') {
return 1;
}
// 尝试从设置获取自定义汇率
$custom_rate = get_option('czl_exchange_rate_' . $wc_currency);
if (!empty($custom_rate)) {
return floatval($custom_rate);
}
// 如果没有自定义汇率使用WooCommerce的汇率转换
if (function_exists('wc_get_price_in_currency')) {
return wc_get_price_in_currency(1, $wc_currency);
}
// 如果都没有,返回默认汇率
$default_rates = array(
'USD' => 0.14, // 1 CNY = 0.14 USD
'EUR' => 0.13, // 1 CNY = 0.13 EUR
'GBP' => 0.11, // 1 CNY = 0.11 GBP
// 添加其他常用货币...
);
return isset($default_rates[$wc_currency]) ? $default_rates[$wc_currency] : 1;
}
/**
* 转换货币
*/
private function convert_currency($amount) {
return $amount * $this->exchange_rate;
}
private function adjust_shipping_rate($rate) {
$adjustment = get_option('czl_rate_adjustment', '');
if (empty($adjustment)) {
return $rate;
}
try {
// 解析调整公式
$formula = strtolower(str_replace(' ', '', $adjustment));
$original_rate = $rate;
// 处理百分比
if (strpos($formula, '%') !== false) {
preg_match('/(\d+)%/', $formula, $matches);
if (!empty($matches[1])) {
$percentage = floatval($matches[1]) / 100;
$rate = $rate * (1 + $percentage);
}
$formula = preg_replace('/\d+%/', '', $formula);
}
// 处理固定金额
if (preg_match('/([+-])(\d+)/', $formula, $matches)) {
$operator = $matches[1];
$amount = floatval($matches[2]);
$rate = $operator === '+' ? $rate + $amount : $rate - $amount;
}
CZL_Logger::debug('Rate adjustment', array(
'original_rate' => $original_rate,
'adjusted_rate' => $rate,
'formula' => $adjustment
));
return max(0, $rate);
} catch (Exception $e) {
CZL_Logger::error('Rate adjustment error', array('error' => $e->getMessage()));
return $rate;
}
}
/**
* 计算单个产品的计费重
* @param WC_Product $product 产品对象
* @param int $quantity 数量
* @return float 计费重
*/
private function calculate_chargeable_weight($product, $quantity) {
// 获取产品尺寸和重量
$length = (float)$product->get_length();
$width = (float)$product->get_width();
$height = (float)$product->get_height();
$actual_weight = (float)$product->get_weight();
// 确保所有值都大于0
$length = max($length, 1);
$width = max($width, 1);
$height = max($height, 1);
$actual_weight = max($actual_weight, 0.1);
// 计算体积重 (长*宽*高/5000)
$volumetric_weight = ($length * $width * $height) / 5000;
// 取实重和体积重中的较大值
$chargeable_weight = max($actual_weight, $volumetric_weight);
// 乘以数量
return $chargeable_weight * $quantity;
}
/**
* 计算运费
*/
public function calculate_shipping_rate($package) {
try {
CZL_Logger::debug('Calculating shipping rate for package', array('package' => $package));
// 基本验证
if (empty($package['destination']['country'])) {
CZL_Logger::warning('Empty destination country');
return array();
}
// 计算总计费重
$total_chargeable_weight = 0;
foreach ($package['contents'] as $item) {
$product = $item['data'];
$quantity = $item['quantity'];
// 累加计费重
$total_chargeable_weight += $this->calculate_chargeable_weight($product, $quantity);
}
// 调用API获取运费
$api_params = array(
'weight' => $total_chargeable_weight, // 使用计算出的总计费重
'country' => $package['destination']['country'],
'postcode' => $package['destination']['postcode'],
'cargoType' => 'P',
'length' => 10, // 添加固定尺寸
'width' => 10, // 添加固定尺寸
'height' => 10 // 添加固定尺寸
);
CZL_Logger::debug('API params with chargeable weight', array('params' => $api_params));
// 调用API获取运费
$api_rates = $this->api->get_shipping_rate($api_params);
CZL_Logger::debug('API response', array('rates' => $api_rates));
if (empty($api_rates)) {
return array();
}
// 按产品分组处理运费
$grouped_rates = array();
$all_rates = array(); // 存储所有线路
foreach ($api_rates as $rate) {
$product_name = isset($rate['product_name']) ? $rate['product_name'] : '';
$product_id = isset($rate['product_id']) ? $rate['product_id'] : '';
$delivery_time = isset($rate['product_aging']) ? $rate['product_aging'] : '';
$amount = isset($rate['total_amount']) ? floatval($rate['total_amount']) : 0;
// 调整运费
$adjusted_amount = $this->adjust_shipping_rate($amount);
// 查找匹配的分组
foreach ($this->product_groups as $group_key => $group) {
foreach ($group['prefixes'] as $prefix) {
if (strpos($product_name, $prefix) === 0) {
$group_id = sanitize_title($group['groupName']);
if (!isset($grouped_rates[$group_id]) ||
!isset($grouped_rates[$group_id]['meta_data']) ||
!isset($grouped_rates[$group_id]['meta_data']['original_amount']) ||
$amount < $grouped_rates[$group_id]['meta_data']['original_amount']) {
// 构建分组运费数据
$grouped_rates[$group_id] = array(
'product_id' => $product_id,
'method_title' => sprintf(
'%s (%s)',
$group['groupName'],
$this->translate_delivery_time($delivery_time)
),
'cost' => $this->convert_currency($adjusted_amount),
'delivery_time' => $delivery_time,
'original_name' => $product_name,
'is_group' => true,
'group_name' => $group['groupName'],
'original_amount' => $amount
);
}
break;
}
}
}
// 构建单独线路运费数据
$all_rates[] = array(
'product_id' => $product_id,
'method_title' => sprintf(
'%s (%s)',
$product_name,
$this->translate_delivery_time($delivery_time)
),
'cost' => $this->convert_currency($adjusted_amount),
'delivery_time' => $delivery_time,
'original_name' => $product_name,
'is_group' => false,
'group_name' => '',
'original_amount' => $amount
);
}
// 根据设置决定返回分组还是所有线路
$show_all_rates = get_option('czl_show_all_rates', 'no');
return $show_all_rates === 'yes' ? array_values($all_rates) : array_values($grouped_rates);
} catch (Exception $e) {
CZL_Logger::error('Failed to calculate shipping rate', array('error' => $e->getMessage()));
wc_add_notice($e->getMessage(), 'error');
return array();
}
}
/**
* 翻译配送时间
*/
private function translate_delivery_time($delivery_time) {
// 获取当前语言环境
$locale = determine_locale();
// 如果是中文环境,保持原样
if (strpos($locale, 'zh_') === 0) {
return $delivery_time;
}
// 匹配中文时效格式
if (preg_match('/(\d+)-(\d+)个工作日/', $delivery_time, $matches)) {
return sprintf('%d-%d working days', $matches[1], $matches[2]);
}
// 匹配"XX个工作日"格式
if (preg_match('/(\d+)个工作日/', $delivery_time, $matches)) {
return sprintf('%d working days', $matches[1]);
}
// 匹配"预计XX天左右"格式
if (preg_match('/预计(\d+)天左右/', $delivery_time, $matches)) {
return sprintf('About %d days', $matches[1]);
}
// 匹配"预计XX-XX天"格式
if (preg_match('/预计(\d+)-(\d+)天/', $delivery_time, $matches)) {
return sprintf('About %d-%d days', $matches[1], $matches[2]);
}
// 匹配"XX-XX天"格式
if (preg_match('/(\d+)-(\d+)天/', $delivery_time, $matches)) {
return sprintf('%d-%d days', $matches[1], $matches[2]);
}
// 匹配"XX天"格式
if (preg_match('/(\d+)天/', $delivery_time, $matches)) {
return sprintf('%d days', $matches[1]);
}
// 其他常见格式的翻译
$translations = array(
'当天送达' => 'Same day delivery',
'次日送达' => 'Next day delivery',
'隔日送达' => 'Second day delivery',
'工作日' => 'working days',
'左右' => 'about',
'预计' => 'About',
'快速' => 'Express',
'标准' => 'Standard'
);
$translated = $delivery_time;
foreach ($translations as $cn => $en) {
$translated = str_replace($cn, $en, $translated);
}
// 如果经过翻译后与原文相同,说明没有匹配到任何规则
if ($translated === $delivery_time) {
CZL_Logger::warning('Unable to translate delivery time', array('delivery_time' => $delivery_time));
}
return $translated;
}
}