first commit

This commit is contained in:
wood chen 2024-12-19 20:38:24 +08:00
commit 6fa8720de1
32 changed files with 3950 additions and 0 deletions

View File

@ -0,0 +1,31 @@
<?php
defined('ABSPATH') || exit;
$tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'settings';
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<nav class="nav-tab-wrapper">
<a href="?page=czl-express&tab=settings" class="nav-tab <?php echo $tab === 'settings' ? 'nav-tab-active' : ''; ?>">
<?php _e('基本设置', 'woo-czl-express'); ?>
</a>
<a href="?page=czl-express&tab=test" class="nav-tab <?php echo $tab === 'test' ? 'nav-tab-active' : ''; ?>">
<?php _e('接口测试', 'woo-czl-express'); ?>
</a>
</nav>
<div class="tab-content">
<?php
switch ($tab) {
case 'test':
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/api-test.php';
break;
default:
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/settings.php';
break;
}
?>
</div>
</div>

View File

@ -0,0 +1,132 @@
<?php
defined('ABSPATH') || exit;
$settings = array(
array(
'title' => __('API Settings', 'woo-czl-express'),
'type' => 'title',
'id' => 'czl_api_settings'
),
array(
'title' => __('API URL', 'woo-czl-express'),
'type' => 'text',
'id' => 'czl_api_url',
'desc' => __('Enter the CZL Express API URL', 'woo-czl-express'),
'default' => ''
),
array(
'title' => __('Username', 'woo-czl-express'),
'type' => 'text',
'id' => 'czl_username',
'desc' => __('Enter your CZL Express API username', 'woo-czl-express'),
'default' => ''
),
array(
'title' => __('Password', 'woo-czl-express'),
'type' => 'password',
'id' => 'czl_password',
'desc' => __('Enter your CZL Express API password', 'woo-czl-express'),
'default' => ''
),
array(
'title' => __('Warehouse Settings', 'woo-czl-express'),
'type' => 'title',
'id' => 'czl_warehouse_settings'
),
array(
'title' => __('Province', 'woo-czl-express'),
'type' => 'text',
'id' => 'czl_warehouse_province',
'desc' => __('Enter warehouse province', 'woo-czl-express'),
'default' => ''
),
array(
'title' => __('City', 'woo-czl-express'),
'type' => 'text',
'id' => 'czl_warehouse_city',
'desc' => __('Enter warehouse city', 'woo-czl-express'),
'default' => ''
),
array(
'title' => __('Address', 'woo-czl-express'),
'type' => 'textarea',
'id' => 'czl_warehouse_address',
'desc' => __('Enter warehouse address', 'woo-czl-express'),
'default' => ''
),
array('type' => 'sectionend', 'id' => 'czl_api_settings'),
);
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields('czl_api_options');
do_settings_sections('czl_api_options');
WC_Admin_Settings::output_fields($settings);
submit_button();
?>
</form>
<div class="czl-api-test">
<h2><?php _e('API Connection Test', 'woo-czl-express'); ?></h2>
<button type="button" class="button" id="czl-test-connection">
<?php _e('Test Connection', 'woo-czl-express'); ?>
</button>
<button type="button" class="button" id="czl-test-shipping-rate">
<?php _e('Test Shipping Rate', 'woo-czl-express'); ?>
</button>
<div id="czl-test-result"></div>
</div>
</div>
<script>
jQuery(function($) {
$('#czl-test-connection').on('click', function() {
var $button = $(this);
var $result = $('#czl-test-result');
$button.prop('disabled', true);
$result.html('<?php _e('Testing...', 'woo-czl-express'); ?>');
$.post(ajaxurl, {
action: 'czl_test_connection',
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
}, function(response) {
$button.prop('disabled', false);
if (response.success) {
$result.html('<div class="notice notice-success"><p>' + response.data.message + '</p></div>');
} else {
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
}
});
});
$('#czl-test-shipping-rate').on('click', function() {
var $button = $(this);
var $result = $('#czl-test-result');
$button.prop('disabled', true);
$result.html('<?php _e('Testing...', 'woo-czl-express'); ?>');
$.post(ajaxurl, {
action: 'czl_test_shipping_rate',
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
}, function(response) {
$button.prop('disabled', false);
if (response.success) {
var html = '<div class="notice notice-success"><p><?php _e('Shipping rates retrieved successfully:', 'woo-czl-express'); ?></p>';
html += '<pre>' + JSON.stringify(response.data, null, 2) + '</pre></div>';
$result.html(html);
} else {
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
}
});
});
});
</script>

69
admin/views/api-test.php Normal file
View File

@ -0,0 +1,69 @@
<?php
defined('ABSPATH') || exit;
?>
<div class="czl-api-test">
<h2><?php _e('API连接测试', 'woo-czl-express'); ?></h2>
<p class="description">
<?php _e('点击下面的按钮测试与CZL Express API的连接。', 'woo-czl-express'); ?>
</p>
<div class="test-buttons">
<button type="button" class="button" id="czl-test-connection">
<?php _e('测试连接', 'woo-czl-express'); ?>
</button>
<button type="button" class="button" id="czl-test-shipping-rate">
<?php _e('测试运费查询', 'woo-czl-express'); ?>
</button>
</div>
<div id="czl-test-result"></div>
</div>
<script>
jQuery(function($) {
$('#czl-test-connection').on('click', function() {
var $button = $(this);
var $result = $('#czl-test-result');
$button.prop('disabled', true);
$result.html('<?php _e('测试中...', 'woo-czl-express'); ?>');
$.post(ajaxurl, {
action: 'czl_test_connection',
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
}, function(response) {
$button.prop('disabled', false);
if (response.success) {
$result.html('<div class="notice notice-success"><p>' + response.data.message + '</p></div>');
} else {
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
}
});
});
$('#czl-test-shipping-rate').on('click', function() {
var $button = $(this);
var $result = $('#czl-test-result');
$button.prop('disabled', true);
$result.html('<?php _e('测试中...', 'woo-czl-express'); ?>');
$.post(ajaxurl, {
action: 'czl_test_shipping_rate',
nonce: '<?php echo wp_create_nonce('czl_test_api'); ?>'
}, function(response) {
$button.prop('disabled', false);
if (response.success) {
var html = '<div class="notice notice-success"><p><?php _e('运费查询成功:', 'woo-czl-express'); ?></p>';
html += '<pre>' + JSON.stringify(response.data, null, 2) + '</pre></div>';
$result.html(html);
} else {
$result.html('<div class="notice notice-error"><p>' + response.data.message + '</p></div>');
}
});
});
});
</script>

View File

@ -0,0 +1,203 @@
<?php
defined('ABSPATH') || exit;
$current_currency = get_woocommerce_currency();
$supported_currencies = get_woocommerce_currencies();
$exchange_rates = array();
// 获取所有已保存的汇率
foreach ($supported_currencies as $code => $name) {
if ($code !== 'CNY') {
$rate = get_option('czl_exchange_rate_' . $code, '');
if ($rate !== '') {
$exchange_rates[$code] = $rate;
}
}
}
?>
<div class="wrap czl-exchange-rates-page">
<h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
<?php settings_errors(); ?>
<div class="czl-page-description">
<p>
<?php _e('在这里设置人民币(CNY)与其他货币的汇率。运费将根据这些汇率自动转换。', 'woo-czl-express'); ?>
<?php printf(__('当前商店使用的货币是: %s', 'woo-czl-express'), '<strong>' . $current_currency . '</strong>'); ?>
</p>
</div>
<form method="post" action="">
<?php wp_nonce_field('czl_save_exchange_rates', 'czl_exchange_rates_nonce'); ?>
<div class="czl-table-container">
<table class="widefat czl-exchange-rates" id="czl-exchange-rates">
<thead>
<tr>
<th class="column-currency"><?php _e('货币', 'woo-czl-express'); ?></th>
<th class="column-rate"><?php _e('汇率 (1 CNY =)', 'woo-czl-express'); ?></th>
<th class="column-actions"><?php _e('操作', 'woo-czl-express'); ?></th>
</tr>
</thead>
<tbody>
<?php if (!empty($exchange_rates)) : ?>
<?php foreach ($exchange_rates as $code => $rate): ?>
<tr>
<td class="column-currency">
<select name="rates[<?php echo esc_attr($code); ?>][currency]" class="currency-select">
<?php
foreach ($supported_currencies as $currency_code => $currency_name) {
if ($currency_code !== 'CNY') {
printf(
'<option value="%s" %s>%s (%s)</option>',
esc_attr($currency_code),
selected($currency_code, $code, false),
esc_html($currency_name),
esc_html($currency_code)
);
}
}
?>
</select>
</td>
<td class="column-rate">
<input type="number" name="rates[<?php echo esc_attr($code); ?>][rate]"
value="<?php echo esc_attr($rate); ?>" step="0.000001" min="0" class="regular-text">
</td>
<td class="column-actions">
<button type="button" class="button remove-rate" title="<?php esc_attr_e('删除此汇率', 'woo-czl-express'); ?>">
<span class="dashicons dashicons-trash"></span>
</button>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<td colspan="3">
<button type="button" class="button add-rate">
<span class="dashicons dashicons-plus-alt2"></span>
<?php _e('添加汇率', 'woo-czl-express'); ?>
</button>
</td>
</tr>
</tfoot>
</table>
</div>
<?php submit_button(__('保存汇率设置', 'woo-czl-express')); ?>
</form>
</div>
<script type="text/template" id="rate-row-template">
<tr>
<td class="column-currency">
<select name="rates[{{index}}][currency]" class="currency-select">
<?php
foreach ($supported_currencies as $code => $name) {
if ($code !== 'CNY') {
printf(
'<option value="%s">%s (%s)</option>',
esc_attr($code),
esc_html($name),
esc_html($code)
);
}
}
?>
</select>
</td>
<td class="column-rate">
<input type="number" name="rates[{{index}}][rate]" value="" step="0.000001" min="0" class="regular-text">
</td>
<td class="column-actions">
<button type="button" class="button remove-rate" title="<?php esc_attr_e('删除此汇率', 'woo-czl-express'); ?>">
<span class="dashicons dashicons-trash"></span>
</button>
</td>
</tr>
</script>
<style>
.czl-exchange-rates-page {
max-width: 800px;
margin: 20px auto;
}
.czl-page-description {
margin: 20px 0;
padding: 15px;
background: #fff;
border-left: 4px solid #2271b1;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.czl-table-container {
margin: 20px 0;
background: #fff;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.czl-exchange-rates {
border: none;
}
.czl-exchange-rates th {
padding: 15px;
font-weight: 600;
}
.czl-exchange-rates td {
padding: 15px;
}
.column-currency {
width: 40%;
}
.column-rate {
width: 40%;
}
.column-actions {
width: 20%;
text-align: center;
}
.currency-select {
width: 100%;
}
.remove-rate .dashicons {
margin-top: 3px;
color: #b32d2e;
}
.add-rate .dashicons {
margin-top: 3px;
margin-right: 5px;
}
</style>
<script>
jQuery(function($) {
var $table = $('#czl-exchange-rates');
var template = $('#rate-row-template').html();
var rateIndex = $table.find('tbody tr').length;
$('.add-rate').on('click', function() {
var newRow = template.replace(/{{index}}/g, rateIndex++);
$table.find('tbody').append(newRow);
});
$table.on('click', '.remove-rate', function() {
var $row = $(this).closest('tr');
$row.fadeOut(300, function() {
$row.remove();
});
});
});
</script>

186
admin/views/orders.php Normal file
View File

@ -0,0 +1,186 @@
<?php
if (!defined('ABSPATH')) {
exit;
}
// 获取订单列表
$orders_query = new WC_Order_Query(array(
'limit' => -1,
'orderby' => 'date',
'order' => 'DESC',
'status' => array('processing', 'completed', 'shipping', 'delivered')
));
$orders = $orders_query->get_orders();
?>
<div class="wrap">
<h1><?php _e('CZL Express 订单管理', 'woo-czl-express'); ?></h1>
<div class="tablenav top">
<div class="alignleft actions">
<select id="bulk-action-selector-top">
<option value="-1"><?php _e('批量操作', 'woo-czl-express'); ?></option>
<option value="create_shipment"><?php _e('创建运单', 'woo-czl-express'); ?></option>
</select>
<button class="button" id="doaction"><?php _e('应用', 'woo-czl-express'); ?></button>
</div>
</div>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<td class="manage-column column-cb check-column">
<input type="checkbox" id="cb-select-all-1">
</td>
<th><?php _e('订单号', 'woo-czl-express'); ?></th>
<th><?php _e('日期', 'woo-czl-express'); ?></th>
<th><?php _e('状态', 'woo-czl-express'); ?></th>
<th><?php _e('收件人', 'woo-czl-express'); ?></th>
<th><?php _e('运单信息', 'woo-czl-express'); ?></th>
<th><?php _e('操作', 'woo-czl-express'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<?php
$tracking_number = $order->get_meta('_czl_tracking_number');
$czl_order_id = $order->get_meta('_czl_order_id');
$reference_number = $order->get_meta('_czl_reference_number');
$label_url = $order->get_meta('_czl_label_url');
$shipping_methods = $order->get_shipping_methods();
$shipping_method = current($shipping_methods);
$is_czl = $shipping_method && strpos($shipping_method->get_method_id(), 'czl_express') !== false;
?>
<?php if ($is_czl): ?>
<tr>
<th scope="row" class="check-column">
<input type="checkbox" name="order_ids[]" value="<?php echo esc_attr($order->get_id()); ?>">
</th>
<td>
<a href="<?php echo esc_url($order->get_edit_order_url()); ?>" target="_blank">
#<?php echo esc_html($order->get_order_number()); ?>
</a>
</td>
<td><?php echo esc_html($order->get_date_created()->date_i18n('Y-m-d H:i:s')); ?></td>
<td><?php echo esc_html(wc_get_order_status_name($order->get_status())); ?></td>
<td>
<?php echo esc_html($order->get_formatted_shipping_full_name()); ?><br>
<?php echo esc_html($order->get_shipping_address_1()); ?>
</td>
<td>
<?php if ($tracking_number): ?>
<strong><?php _e('运单号:', 'woo-czl-express'); ?></strong>
<a href="https://exp.czl.net/track/?query=<?php echo esc_attr($tracking_number); ?>" target="_blank">
<?php echo esc_html($tracking_number); ?>
</a><br>
<strong><?php _e('CZL订单号:', 'woo-czl-express'); ?></strong>
<?php echo esc_html($czl_order_id); ?><br>
<strong><?php _e('参考号:', 'woo-czl-express'); ?></strong>
<?php echo esc_html($reference_number); ?><br>
<?php if ($label_url): ?>
<a href="<?php echo esc_url($label_url); ?>" target="_blank" class="button">
<?php _e('打印标签', 'woo-czl-express'); ?>
</a>
<?php endif; ?>
<?php else: ?>
<?php _e('未创建运单', 'woo-czl-express'); ?>
<?php endif; ?>
</td>
<td>
<?php if (!$tracking_number): ?>
<button class="button create-shipment" data-order-id="<?php echo esc_attr($order->get_id()); ?>">
<?php _e('创建运单', 'woo-czl-express'); ?>
</button>
<?php endif; ?>
</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 单个订单创建运单
$('.create-shipment').click(function() {
var button = $(this);
var orderId = button.data('order-id');
button.prop('disabled', true).text('<?php _e('处理中...', 'woo-czl-express'); ?>');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'czl_create_shipment',
order_id: orderId,
nonce: '<?php echo wp_create_nonce('czl_create_shipment'); ?>'
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert(response.data.message);
button.prop('disabled', false).text('<?php _e('创建运单', 'woo-czl-express'); ?>');
}
},
error: function() {
alert('<?php _e('请求失败,请重试', 'woo-czl-express'); ?>');
button.prop('disabled', false).text('<?php _e('创建运单', 'woo-czl-express'); ?>');
}
});
});
// 批量创建运单
$('#doaction').click(function(e) {
e.preventDefault();
var action = $('#bulk-action-selector-top').val();
if (action !== 'create_shipment') {
return;
}
var orderIds = [];
$('input[name="order_ids[]"]:checked').each(function() {
orderIds.push($(this).val());
});
if (orderIds.length === 0) {
alert('<?php _e('请选择订单', 'woo-czl-express'); ?>');
return;
}
if (!confirm('<?php _e('确定要为选中的订单创建运单吗?', 'woo-czl-express'); ?>')) {
return;
}
$(this).prop('disabled', true).text('<?php _e('处理中...', 'woo-czl-express'); ?>');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'czl_bulk_create_shipment',
order_ids: orderIds,
nonce: '<?php echo wp_create_nonce('czl_bulk_create_shipment'); ?>'
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert(response.data.message);
}
},
error: function() {
alert('<?php _e('请求失败,请重试', 'woo-czl-express'); ?>');
},
complete: function() {
$('#doaction').prop('disabled', false).text('<?php _e('应用', 'woo-czl-express'); ?>');
}
});
});
});
</script>

View File

@ -0,0 +1,195 @@
<?php
defined('ABSPATH') || exit;
$groups = get_option('czl_product_groups', array());
?>
<div class="wrap czl-product-groups-page">
<h1 class="wp-heading-inline"><?php echo esc_html(get_admin_page_title()); ?></h1>
<?php settings_errors(); ?>
<div class="czl-page-description">
<p><?php _e('在这里管理运输方式的分组显示。相同分组的运输方式将合并显示,并显示最低价格。', 'woo-czl-express'); ?></p>
</div>
<form method="post" action="">
<?php wp_nonce_field('czl_save_product_groups', 'czl_product_groups_nonce'); ?>
<div class="czl-table-container">
<table class="widefat czl-product-groups" id="czl-product-groups">
<thead>
<tr>
<th class="column-enabled"><?php _e('启用', 'woo-czl-express'); ?></th>
<th class="column-name"><?php _e('分组名称', 'woo-czl-express'); ?></th>
<th class="column-prefixes"><?php _e('匹配前缀', 'woo-czl-express'); ?></th>
<th class="column-actions"><?php _e('操作', 'woo-czl-express'); ?></th>
</tr>
</thead>
<tbody>
<?php if (!empty($groups)) : ?>
<?php foreach ($groups as $key => $group): ?>
<tr>
<td class="column-enabled">
<input type="checkbox" name="groups[<?php echo esc_attr($key); ?>][enabled]"
value="1" <?php checked(!empty($group['enabled'])); ?>>
</td>
<td class="column-name">
<input type="text" name="groups[<?php echo esc_attr($key); ?>][groupName]"
value="<?php echo esc_attr($group['groupName']); ?>" class="regular-text">
</td>
<td class="column-prefixes">
<textarea name="groups[<?php echo esc_attr($key); ?>][prefixes]" rows="3" class="large-text"
><?php echo esc_textarea(implode("\n", $group['prefixes'])); ?></textarea>
<p class="description"><?php _e('每行输入一个前缀,运输方式名称以此前缀开头将被归入此分组', 'woo-czl-express'); ?></p>
</td>
<td class="column-actions">
<button type="button" class="button remove-group" title="<?php esc_attr_e('删除此分组', 'woo-czl-express'); ?>">
<span class="dashicons dashicons-trash"></span>
</button>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr class="no-items">
<td colspan="4"><?php _e('没有找到分组配置', 'woo-czl-express'); ?></td>
</tr>
<?php endif; ?>
</tbody>
<tfoot>
<tr>
<td colspan="4">
<button type="button" class="button add-group">
<span class="dashicons dashicons-plus-alt2"></span>
<?php _e('添加分组', 'woo-czl-express'); ?>
</button>
</td>
</tr>
</tfoot>
</table>
</div>
<?php submit_button(__('保存分组设置', 'woo-czl-express'), 'primary', 'submit', true); ?>
</form>
</div>
<script type="text/template" id="group-row-template">
<tr>
<td class="column-enabled">
<input type="checkbox" name="groups[{{index}}][enabled]" value="1" checked>
</td>
<td class="column-name">
<input type="text" name="groups[{{index}}][groupName]" value="" class="regular-text" placeholder="<?php esc_attr_e('输入分组名称', 'woo-czl-express'); ?>">
</td>
<td class="column-prefixes">
<textarea name="groups[{{index}}][prefixes]" rows="3" class="large-text" placeholder="<?php esc_attr_e('每行输入一个前缀', 'woo-czl-express'); ?>"></textarea>
<p class="description"><?php _e('每行输入一个前缀,运输方式名称以此前缀开头将被归入此分组', 'woo-czl-express'); ?></p>
</td>
<td class="column-actions">
<button type="button" class="button remove-group" title="<?php esc_attr_e('删除此分组', 'woo-czl-express'); ?>">
<span class="dashicons dashicons-trash"></span>
</button>
</td>
</tr>
</script>
<style>
.czl-product-groups-page {
max-width: 1200px;
margin: 20px auto;
}
.czl-page-description {
margin: 20px 0;
padding: 15px;
background: #fff;
border-left: 4px solid #2271b1;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.czl-table-container {
margin: 20px 0;
background: #fff;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.czl-product-groups {
border: none;
}
.czl-product-groups th {
padding: 15px;
font-weight: 600;
}
.czl-product-groups td {
vertical-align: top;
padding: 15px;
}
.column-enabled {
width: 60px;
text-align: center;
}
.column-name {
width: 200px;
}
.column-actions {
width: 80px;
text-align: center;
}
.remove-group .dashicons {
margin-top: 3px;
color: #b32d2e;
}
.add-group .dashicons {
margin-top: 3px;
margin-right: 5px;
}
.no-items td {
text-align: center;
padding: 20px !important;
background: #f8f8f8;
}
textarea {
min-height: 80px;
}
.description {
margin-top: 5px;
color: #666;
}
</style>
<script>
jQuery(function($) {
var $table = $('#czl-product-groups');
var template = $('#group-row-template').html();
var groupIndex = $table.find('tbody tr').length;
$('.add-group').on('click', function() {
var newRow = template.replace(/{{index}}/g, groupIndex++);
if ($table.find('.no-items').length) {
$table.find('.no-items').remove();
}
$table.find('tbody').append(newRow);
});
$table.on('click', '.remove-group', function() {
var $row = $(this).closest('tr');
$row.fadeOut(300, function() {
$row.remove();
if ($table.find('tbody tr').length === 0) {
$table.find('tbody').append('<tr class="no-items"><td colspan="4"><?php _e('没有找到分组配置', 'woo-czl-express'); ?></td></tr>');
}
});
});
});
</script>

16
admin/views/settings.php Normal file
View File

@ -0,0 +1,16 @@
<?php
defined('ABSPATH') || exit;
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form method="post" action="options.php">
<?php
settings_fields('czl_options_group');
do_settings_sections('czl_options');
submit_button();
?>
</form>
</div>
</rewritten_file>

172
api.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,30 @@
.shipping-method-group .toggle-sub-methods {
cursor: pointer;
margin-left: 5px;
color: #666;
}
.shipping-method-group .sub-methods {
margin-top: 10px;
padding: 10px;
background: #f8f8f8;
border-radius: 4px;
}
.shipping-method-group .sub-methods-table {
width: 100%;
border-collapse: collapse;
}
.shipping-method-group .sub-methods-table th,
.shipping-method-group .sub-methods-table td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.shipping-method-group .remote-fee-notice {
font-size: 0.9em;
color: #e2401c;
margin-top: 5px;
}

View File

@ -0,0 +1,60 @@
.czl-express-rate {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.czl-express-rate .delivery-time {
color: #666;
font-size: 0.9em;
}
.czl-express-rate .rate-price {
font-weight: bold;
}
.czl-express-group {
margin-bottom: 10px;
}
.czl-express-group-header {
cursor: pointer;
padding: 8px;
background: #f8f8f8;
border-radius: 4px;
}
.czl-express-group-content {
padding-left: 20px;
display: none;
}
.czl-express-group.expanded .czl-express-group-content {
display: block;
}
.czl-tracking-info {
margin: 20px 0;
}
.czl-tracking-history {
list-style: none;
margin: 0;
padding: 0;
}
.czl-tracking-history li {
padding: 10px 0;
border-bottom: 1px solid #eee;
}
.czl-tracking-history .tracking-date {
color: #666;
margin-right: 10px;
}
.czl-tracking-history .tracking-location {
color: #999;
margin-left: 10px;
}

View File

@ -0,0 +1,11 @@
jQuery(function($) {
$('.shipping-method-group .toggle-sub-methods').on('click', function(e) {
e.preventDefault();
var $group = $(this).closest('.shipping-method-group');
var $subMethods = $group.find('.sub-methods');
$subMethods.slideToggle();
$(this).toggleClass('dashicons-arrow-down-alt2 dashicons-arrow-up-alt2');
});
});

View File

@ -0,0 +1,41 @@
<?php
class CZL_Ajax {
public function __construct() {
add_action('wp_ajax_czl_test_connection', array($this, 'test_connection'));
add_action('wp_ajax_czl_test_shipping_rate', array($this, 'test_shipping_rate'));
}
public function test_connection() {
check_ajax_referer('czl_test_api', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(array('message' => __('Permission denied', 'woo-czl-express')));
}
$tester = new CZL_API_Test();
$result = $tester->test_connection();
if ($result['success']) {
wp_send_json_success($result);
} else {
wp_send_json_error($result);
}
}
public function test_shipping_rate() {
check_ajax_referer('czl_test_api', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(array('message' => __('Permission denied', 'woo-czl-express')));
}
$tester = new CZL_API_Test();
$result = $tester->test_shipping_rate();
if ($result['success']) {
wp_send_json_success($result['data']);
} else {
wp_send_json_error($result);
}
}
}

View File

@ -0,0 +1,67 @@
<?php
class CZL_API_Test {
private $api;
public function __construct() {
$this->api = new CZL_API();
}
/**
* 测试API连接
*/
public function test_connection() {
try {
// 尝试获取token
$this->api->get_token();
return array(
'success' => true,
'message' => __('Successfully connected to CZL Express API', 'woo-czl-express')
);
} catch (Exception $e) {
return array(
'success' => false,
'message' => $e->getMessage()
);
}
}
/**
* 测试运费查询
*/
public function test_shipping_rate() {
$test_package = array(
'destination' => array(
'country' => 'US',
'state' => 'CA',
'city' => 'Los Angeles',
'address' => '123 Test St',
'postcode' => '90001'
),
'contents' => array(
array(
'data' => new WC_Product_Simple(array(
'weight' => 1,
'length' => 10,
'width' => 10,
'height' => 10,
'price' => 100
)),
'quantity' => 1
)
)
);
try {
$rates = $this->api->get_shipping_rate($test_package);
return array(
'success' => true,
'data' => $rates
);
} catch (Exception $e) {
return array(
'success' => false,
'message' => $e->getMessage()
);
}
}
}

505
includes/class-czl-api.php Normal file
View File

@ -0,0 +1,505 @@
<?php
class CZL_API {
private $api_url;
private $username;
private $password;
private $token;
private $token_expires;
private $country_mapping;
private $customer_id;
private $customer_userid;
public function __construct() {
$this->api_url = get_option('czl_api_url', '');
$this->username = get_option('czl_username', '');
$this->password = get_option('czl_password', '');
$this->token = get_transient('czl_api_token');
$this->token_expires = get_transient('czl_api_token_expires');
$this->init_country_mapping();
}
/**
* 初始化国家代码映射
*/
private function init_country_mapping() {
// 从API获取国家列表并缓存
$cached_mapping = get_transient('czl_country_mapping');
if ($cached_mapping !== false) {
$this->country_mapping = $cached_mapping;
return;
}
try {
$response = wp_remote_get('https://tms-api-go.czl.net/api/countries', array(
'timeout' => 30
));
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if (!empty($data['data'])) {
$mapping = array();
foreach ($data['data'] as $country) {
$mapping[$country['ename']] = $country['code'];
}
$this->country_mapping = $mapping;
set_transient('czl_country_mapping', $mapping, DAY_IN_SECONDS);
error_log('CZL Express: Country mapping updated');
}
} catch (Exception $e) {
error_log('CZL Express Error: Failed to get country mapping - ' . $e->getMessage());
$this->country_mapping = array();
}
}
/**
* 转换国家代码
*/
private function convert_country_code($wc_country) {
error_log('CZL Express: Converting country code ' . $wc_country);
// 获取WooCommerce国家名称
$countries = WC()->countries->get_countries();
$country_name = isset($countries[$wc_country]) ? $countries[$wc_country] : '';
// 在映射中查找
foreach ($this->country_mapping as $name => $code) {
if (stripos($name, $country_name) !== false || stripos($country_name, $name) !== false) {
error_log('CZL Express: Found country mapping ' . $wc_country . ' => ' . $code);
return $code;
}
}
// 如果没找到映射,返回原始代码
error_log('CZL Express: No mapping found for ' . $wc_country . ', using original code');
return $wc_country;
}
/**
* 获取API认证Token
*/
private function get_token() {
if ($this->token && time() < $this->token_expires) {
return $this->token;
}
$response = wp_remote_post($this->api_url . '/auth/login', array(
'body' => array(
'username' => $this->username,
'password' => md5($this->password) // CZL API需要MD5加密的密码
),
'timeout' => 30
));
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (empty($body['success'])) {
throw new Exception(!empty($body['message']) ? $body['message'] : '认证失败');
}
$this->token = $body['data']['token'];
$this->token_expires = time() + 7200; // CZL token有效期通常是2小时
set_transient('czl_api_token', $this->token, 7200);
set_transient('czl_api_token_expires', $this->token_expires, 7200);
return $this->token;
}
/**
* 发送API请求
*/
private function request($endpoint, $method = 'GET', $data = null) {
$args = array(
'method' => $method,
'headers' => array(
'Authorization' => $this->get_token(),
'Content-Type' => 'application/json'
),
'timeout' => 30
);
if ($data !== null) {
$args['body'] = json_encode($data);
}
$response = wp_remote_request($this->api_url . $endpoint, $args);
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (empty($body['success'])) {
throw new Exception(!empty($body['message']) ? $body['message'] : '请求失败');
}
return $body['data'];
}
/**
* 获取运费报价
*/
public function get_shipping_rate($params) {
try {
$api_url = 'https://tms.czl.net/defaultPriceSearchJson.htm';
// 转换国家代码
$country_code = $this->convert_country_code($params['country']);
// 构建请求参数
$query = array(
'weight' => $params['weight'],
'country' => $country_code,
'cargoType' => $params['cargoType'],
'length' => $params['length'],
'width' => $params['width'],
'height' => $params['height'],
'postcode' => $params['postcode']
);
error_log('CZL Express: Shipping rate request - ' . print_r($query, true));
// 发送请求
$response = wp_remote_post($api_url . '?' . http_build_query($query), array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode($this->username . ':' . $this->password)
),
'timeout' => 30
));
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
error_log('CZL Express: API raw response - ' . $body);
$data = json_decode($body, true);
if (empty($data)) {
throw new Exception('Empty API response');
}
// 检查响应格式
if (!is_array($data)) {
throw new Exception('Invalid API response format');
}
return $data;
} catch (Exception $e) {
error_log('CZL Express API Error: ' . $e->getMessage());
throw $e;
}
}
/**
* 格式化运费报价结果
*/
private function format_shipping_rates($rates) {
$formatted_rates = array();
foreach ($rates as $rate) {
$formatted_rates[] = array(
'method_id' => 'czl_express_' . sanitize_title($rate['product_id']),
'method_title' => $rate['product_name'],
'method_name' => $rate['product_name'],
'cost' => floatval($rate['total_amount']),
'delivery_time' => $rate['product_aging'],
'product_id' => $rate['product_id'],
'product_note' => $rate['product_note']
);
}
return $formatted_rates;
}
/**
* 创建运单
*/
public function create_order($order_data) {
try {
// 添加请求前的日志
error_log('CZL Express: Creating order with data - ' . print_r($order_data, true));
$response = wp_remote_post('https://tms.czl.net/createOrderApi.htm', array(
'body' => array(
'Param' => json_encode($order_data)
),
'timeout' => 30
));
// 添加响应日志
error_log('CZL Express: Raw API response - ' . print_r($response, true));
if (is_wp_error($response)) {
throw new Exception('API请求失败: ' . $response->get_error_message());
}
$result = json_decode(wp_remote_retrieve_body($response), true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('JSON解析失败: ' . json_last_error_msg());
}
if (empty($result['ack']) || $result['ack'] !== 'true') {
throw new Exception(!empty($result['message']) ? urldecode($result['message']) : '未知错误');
}
return $result;
} catch (Exception $e) {
error_log('CZL Express Error: Create order failed - ' . $e->getMessage());
error_log('CZL Express Error Stack Trace: ' . $e->getTraceAsString());
throw $e;
}
}
/**
* 获取运单跟踪信息
*/
public function get_tracking($tracking_number) {
try {
$response = wp_remote_post('https://tms.czl.net/selectTrack.htm', array(
'body' => array(
'documentCode' => $tracking_number
),
'timeout' => 30
));
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$result = json_decode(wp_remote_retrieve_body($response), true);
if (empty($result[0]['ack']) || $result[0]['ack'] !== 'true') {
throw new Exception('获取跟踪信息失败');
}
return $result[0]['data'][0];
} catch (Exception $e) {
error_log('CZL Express API Error: ' . $e->getMessage());
throw $e;
}
}
/**
* 获取运单标签
*/
public function get_label($order_id) {
$url = sprintf(
'https://tms-label.czl.net/order/FastRpt/PDF_NEW.aspx?Format=lbl_sub一票多件161810499441.frx&PrintType=1&order_id=%s',
$order_id
);
return $url;
}
/**
* 取消运单
*/
public function cancel_order($order_number) {
return $this->request('/shipping/cancel', 'POST', array(
'order_number' => $order_number
));
}
/**
* 测试认证
*/
public function test_auth() {
try {
$response = wp_remote_post('https://tms.czl.net/selectAuth.htm', array(
'body' => array(
'username' => $this->username,
'password' => $this->password
),
'timeout' => 30
));
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$result = json_decode(wp_remote_retrieve_body($response), true);
if (empty($result['success'])) {
throw new Exception('认证失败');
}
return array(
'customer_id' => $result['customer_id'],
'customer_userid' => $result['customer_userid']
);
} catch (Exception $e) {
error_log('CZL Express API Error: ' . $e->getMessage());
throw $e;
}
}
/**
* 获取支持的国家列表
*/
public function get_countries() {
try {
$response = wp_remote_get('https://tms-api-go.czl.net/api/countries', array(
'timeout' => 30
));
if (is_wp_error($response)) {
throw new Exception($response->get_error_message());
}
$result = json_decode(wp_remote_retrieve_body($response), true);
if ($result['code'] !== 200) {
throw new Exception('获取国家列表失败');
}
return $result['data'];
} catch (Exception $e) {
error_log('CZL Express API Error: ' . $e->getMessage());
throw $e;
}
}
private function ensure_logged_in() {
try {
error_log('CZL Express: Starting authentication');
$auth_url = 'https://tms.czl.net/selectAuth.htm';
$auth_data = array(
'username' => $this->username,
'password' => $this->password
);
error_log('CZL Express: Auth request data - ' . print_r($auth_data, true));
$ch = curl_init($auth_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($auth_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Accept-Language: zh-cn',
'Connection: Keep-Alive',
'Cache-Control: no-cache'
));
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$response = curl_exec($ch);
error_log('CZL Express: Auth raw response - ' . $response);
if (curl_errno($ch)) {
throw new Exception('认证请求失败');
}
curl_close($ch);
// 解析响应
$result = json_decode(str_replace("'", '"', $response), true);
error_log('CZL Express: Auth decoded response - ' . print_r($result, true));
if (empty($result) || !isset($result['customer_id'])) {
throw new Exception('认证失败');
}
// 保存认证信息
$this->customer_id = $result['customer_id'];
$this->customer_userid = $result['customer_userid'];
} catch (Exception $e) {
error_log('CZL Express Error: Authentication failed - ' . $e->getMessage());
throw new Exception('认证失败请联系CZL Express');
}
}
public function create_shipment($data) {
try {
error_log('CZL Express: Starting create shipment');
// 确保已登录
$this->ensure_logged_in();
// 使用类属性中的认证信息
$data['customer_id'] = $this->customer_id;
$data['customer_userid'] = $this->customer_userid;
$api_url = 'https://tms.czl.net/createOrderApi.htm';
// 构建请求数据
$request_data = array(
'param' => json_encode($data, JSON_UNESCAPED_UNICODE)
);
error_log('CZL Express: Create shipment request data - ' . print_r($request_data, true));
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Accept-Language: zh-cn',
'Connection: Keep-Alive',
'Cache-Control: no-cache',
'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
));
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
// 添加更多调试信息
curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
$response = curl_exec($ch);
// 记录详细的curl信息
if (curl_errno($ch)) {
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
error_log('CZL Express: Curl verbose log - ' . $verboseLog);
error_log('CZL Express: Curl error - ' . curl_error($ch));
throw new Exception('请求失败: ' . curl_error($ch));
}
error_log('CZL Express: Create shipment raw response - ' . $response);
error_log('CZL Express: Response info - ' . print_r(curl_getinfo($ch), true));
curl_close($ch);
fclose($verbose);
// 检查响应是否为空
if (empty($response)) {
throw new Exception('服务器未返回数据');
}
// 解析响应
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log('CZL Express: JSON decode error - ' . json_last_error_msg());
throw new Exception('响应数据格式错误');
}
error_log('CZL Express: Create shipment decoded response - ' . print_r($result, true));
if (!isset($result['ack']) || $result['ack'] !== 'true') {
$error_msg = isset($result['message']) ? urldecode($result['message']) : '未知错误';
error_log('CZL Express: API error message - ' . $error_msg);
throw new Exception($error_msg);
}
return $result;
} catch (Exception $e) {
error_log('CZL Express Error: Create shipment failed - ' . $e->getMessage());
error_log('CZL Express Error Stack Trace: ' . $e->getTraceAsString());
throw $e;
}
}
}

View File

@ -0,0 +1,52 @@
<?php
class CZL_Install {
public static function init() {
self::create_tables();
self::create_options();
}
public static function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 创建运单表
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}czl_shipments (
id bigint(20) NOT NULL AUTO_INCREMENT,
order_id bigint(20) NOT NULL,
tracking_number varchar(50) NOT NULL,
label_url varchar(255) DEFAULT NULL,
shipping_method varchar(100) NOT NULL,
status varchar(50) DEFAULT 'pending',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY order_id (order_id),
KEY tracking_number (tracking_number)
) $charset_collate;";
// 创建运费规则表
$sql .= "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}czl_shipping_rules (
id bigint(20) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
shipping_method varchar(100) NOT NULL,
czl_product_code varchar(100) NOT NULL,
price_adjustment varchar(255) DEFAULT NULL,
status int(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
public static function create_options() {
// 添加默认配置选项
add_option('czl_api_url', '');
add_option('czl_username', '');
add_option('czl_password', '');
add_option('czl_exchange_rate', '1');
add_option('czl_product_groups', '');
}
}

View File

@ -0,0 +1,53 @@
<?php
class CZL_Label {
/**
* 获取标签URL
*/
public static function get_label_url($order_id) {
$czl_order_id = get_post_meta($order_id, '_czl_order_id', true);
if (!$czl_order_id) {
return false;
}
$api = new CZL_API();
return $api->get_label($czl_order_id);
}
/**
* 添加标签打印按钮
*/
public static function add_print_actions($actions, $order) {
if (self::get_label_url($order->get_id())) {
$actions['czl_print_label'] = array(
'url' => wp_nonce_url(admin_url('admin-ajax.php?action=czl_print_label&order_id=' . $order->get_id()), 'czl_print_label'),
'name' => __('打印运单', 'woo-czl-express'),
'action' => 'czl_print_label'
);
}
return $actions;
}
/**
* 处理标签打印请求
*/
public static function handle_print_request() {
if (!current_user_can('edit_shop_orders')) {
wp_die(__('您没有权限执行此操作', 'woo-czl-express'));
}
check_admin_referer('czl_print_label');
$order_id = isset($_GET['order_id']) ? absint($_GET['order_id']) : 0;
if (!$order_id) {
wp_die(__('订单ID无效', 'woo-czl-express'));
}
$label_url = self::get_label_url($order_id);
if (!$label_url) {
wp_die(__('未找到运单标签', 'woo-czl-express'));
}
wp_redirect($label_url);
exit;
}
}

View File

@ -0,0 +1,87 @@
<?php
class CZL_Order_Data {
private $order;
private $customer_id;
private $customer_userid;
public function __construct($order) {
$this->order = $order;
// 获取认证信息
$auth = get_option('czl_auth_info', array());
$this->customer_id = !empty($auth['customer_id']) ? $auth['customer_id'] : '';
$this->customer_userid = !empty($auth['customer_userid']) ? $auth['customer_userid'] : '';
}
/**
* 准备创建运单的数据
*/
public function prepare() {
$shipping_address = $this->order->get_shipping_address_1();
if ($this->order->get_shipping_address_2()) {
$shipping_address .= ' ' . $this->order->get_shipping_address_2();
}
// 基础订单数据
$data = array(
'buyerid' => '',
'consignee_address' => $shipping_address,
'order_piece' => 1, // 默认1件
'consignee_city' => $this->order->get_shipping_city(),
'consignee_mobile' => $this->order->get_shipping_phone(),
'order_returnsign' => 'N',
'consignee_name' => $this->order->get_shipping_first_name() . ' ' . $this->order->get_shipping_last_name(),
'trade_type' => 'ZYXT',
'consignee_postcode' => $this->order->get_shipping_postcode(),
'consignee_state' => $this->order->get_shipping_state(),
'consignee_telephone' => $this->order->get_shipping_phone(),
'country' => $this->order->get_shipping_country(),
'customer_id' => $this->customer_id,
'customer_userid' => $this->customer_userid,
'order_customerinvoicecode' => $this->order->get_order_number(),
'product_id' => $this->get_shipping_product_id(),
'consignee_email' => $this->order->get_billing_email(),
'consignee_companyname' => $this->order->get_shipping_company(),
'order_cargoamount' => $this->order->get_total(),
'orderInvoiceParam' => $this->prepare_items()
);
return $data;
}
/**
* 准备商品信息
*/
private function prepare_items() {
$items = array();
$order_items = $this->order->get_items();
foreach ($order_items as $item) {
$product = $item->get_product();
$items[] = array(
'invoice_amount' => $item->get_total(),
'invoice_pcs' => $item->get_quantity(),
'invoice_title' => $product->get_meta('_czl_name_en') ?: $product->get_name(),
'invoice_weight' => $product->get_weight(),
'sku' => $product->get_name(),
'hs_code' => $product->get_meta('_czl_hs_code')
);
}
return $items;
}
/**
* 获取配送方式ID
*/
private function get_shipping_product_id() {
$shipping_methods = $this->order->get_shipping_methods();
foreach ($shipping_methods as $shipping_method) {
if (strpos($shipping_method->get_method_id(), 'czl_express_') === 0) {
return str_replace('czl_express_', '', $shipping_method->get_method_id());
}
}
return '';
}
}

View File

@ -0,0 +1,121 @@
<?php
class CZL_Order_Handler {
private $api;
public function __construct() {
$this->api = new CZL_API();
}
/**
* 创建运单
*/
public function create_shipment($order_id) {
try {
$order = wc_get_order($order_id);
if (!$order) {
return;
}
// 检查是否已创建运单
$tracking_number = get_post_meta($order_id, '_czl_tracking_number', true);
if ($tracking_number) {
return;
}
// 检查是否使用CZL Express配送
if (!$this->is_czl_shipping($order)) {
return;
}
// 使用统一的CZL_Order类创建运单
$czl_order = new CZL_Order();
$result = $czl_order->create_shipment($order_id);
return $result;
} catch (Exception $e) {
error_log('CZL Express Error: ' . $e->getMessage());
if (isset($order)) {
$order->add_order_note(
sprintf(
__('CZL Express运单创建失败: %s', 'woo-czl-express'),
$e->getMessage()
),
true
);
}
throw $e;
}
}
/**
* 检查是否使用CZL Express配送
*/
private function is_czl_shipping($order) {
$shipping_methods = $order->get_shipping_methods();
foreach ($shipping_methods as $shipping_method) {
if (strpos($shipping_method->get_method_id(), 'czl_express_') === 0) {
return true;
}
}
return false;
}
/**
* 取消运单
*/
public function cancel_shipment($order_id) {
$order = wc_get_order($order_id);
if (!$order) {
return;
}
$czl_order_id = get_post_meta($order_id, '_czl_order_id', true);
if (!$czl_order_id) {
return;
}
try {
$response = $this->api->cancel_order($czl_order_id);
// 添加订单备注
$note = __('CZL Express运单已取消', 'woo-czl-express');
$order->add_order_note($note);
// 清除运单信息
delete_post_meta($order_id, '_czl_order_id');
delete_post_meta($order_id, '_czl_tracking_number');
delete_post_meta($order_id, '_czl_label_url');
} catch (Exception $e) {
$error_message = sprintf(
__('CZL Express运单取消失败: %s', 'woo-czl-express'),
$e->getMessage()
);
$order->add_order_note($error_message);
error_log('CZL Express Error: ' . $error_message);
}
}
public function process_order_action($order) {
try {
error_log('CZL Express: Processing order action for order ' . $order->get_id());
// 检查是否已经创建过运单
$tracking_number = $order->get_meta('_czl_tracking_number');
if (!empty($tracking_number)) {
error_log('CZL Express: Order already has tracking number: ' . $tracking_number);
return;
}
// 创建运单
$czl_order = new CZL_Order();
$result = $czl_order->create_shipment($order->get_id());
error_log('CZL Express: Create shipment result - ' . print_r($result, true));
} catch (Exception $e) {
error_log('CZL Express Error: Failed to process order action - ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,201 @@
<?php
// 确保没有命名空间冲突
if (!class_exists('CZL_Order')) {
class CZL_Order {
private $api;
public function __construct() {
$this->api = new CZL_API();
}
/**
* 创建运单
*/
public function create_shipment($order_id) {
try {
$order = wc_get_order($order_id);
if (!$order) {
throw new Exception('订单不存在');
}
// 检查是否已有运单
$tracking_number = $order->get_meta('_czl_tracking_number');
if (!empty($tracking_number)) {
throw new Exception('订单已存在运单号');
}
// 获取运输方式信息
$shipping_methods = $order->get_shipping_methods();
$shipping_method = current($shipping_methods);
// 从配送方式元数据中获取product_id
$product_id = $shipping_method->get_meta('product_id');
if (empty($product_id)) {
throw new Exception('未找到运输方式ID');
}
// 准备运单数据
$shipment_data = array(
'buyerid' => $order->get_id(),
'consignee_address' => $order->get_shipping_address_1() . ' ' . $order->get_shipping_address_2(),
'consignee_city' => $order->get_shipping_city(),
'consignee_mobile' => $order->get_billing_phone(),
'consignee_name' => $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name(),
'consignee_postcode' => $order->get_shipping_postcode(),
'consignee_state' => $order->get_shipping_state(),
'consignee_email' => $order->get_billing_email(),
'country' => $order->get_shipping_country(),
'order_piece' => $this->get_order_items_count($order),
'product_id' => $product_id,
'trade_type' => 'ZYXT',
'weight' => $this->get_order_weight($order),
'orderInvoiceParam' => array()
);
// 添加发票信息
foreach ($order->get_items() as $item) {
$product = $item->get_product();
if (!$product) {
continue;
}
// 获取海关编码
$hs_code = $product->get_meta('_czl_hs_code');
if (empty($hs_code)) {
throw new Exception('产品 ' . $product->get_name() . ' 缺少海关编码');
}
$shipment_data['orderInvoiceParam'][] = array(
'invoice_amount' => $item->get_total(),
'invoice_pcs' => $item->get_quantity(),
'invoice_title' => $item->get_name(),
'invoice_weight' => $product->get_weight() * $item->get_quantity(),
'item_id' => $product->get_id(),
'sku' => $product->get_sku(),
'hs_code' => $hs_code // 添加海关编码
);
}
// 调用API创建运单
$result = $this->api->create_shipment($shipment_data);
if (!empty($result['tracking_number'])) {
// 更新订单元数据
$order->update_meta_data('_czl_tracking_number', $result['tracking_number']);
$order->update_meta_data('_czl_order_id', $result['order_id']);
$order->update_meta_data('_czl_reference_number', $result['reference_number']);
$order->update_meta_data('_czl_order_privatecode', $result['order_privatecode']);
$order->update_meta_data('_czl_order_transfercode', $result['order_transfercode']);
$order->update_meta_data('_czl_label_url', $result['label_url']);
// 添加订单备注
$order->add_order_note(
sprintf(
__('CZL Express运单创建成功。
运单号: %s
订单号: %s
参考号: %s',
'woo-czl-express'),
$result['tracking_number'],
$result['order_id'],
$result['reference_number']
),
true
);
// 更新订单状态
$order->update_status('shipping', __('运单已创建,包裹开始运输', 'woo-czl-express'));
$order->save();
}
return $result;
} catch (Exception $e) {
error_log('CZL Express Error: ' . $e->getMessage());
throw $e;
}
}
/**
* 获取订单总重量
*/
private function get_order_weight($order) {
$total_weight = 0;
foreach ($order->get_items() as $item) {
$product = $item->get_product();
if ($product && $product->get_weight()) {
$total_weight += ($product->get_weight() * $item->get_quantity());
}
}
return max(0.1, $total_weight); // 最小重量0.1kg
}
private function get_order_items_count($order) {
$count = 0;
foreach ($order->get_items() as $item) {
$count += $item->get_quantity();
}
return max(1, $count);
}
private function prepare_invoice_items($order) {
$items = array();
foreach ($order->get_items() as $item) {
$product = $item->get_product();
$items[] = array(
'invoice_amount' => $item->get_total(),
'invoice_pcs' => $item->get_quantity(),
'invoice_title' => $item->get_name(),
'invoice_weight' => $product ? ($product->get_weight() * $item->get_quantity()) : 0.1,
'item_id' => $product ? $product->get_id() : '',
'sku' => $product ? $product->get_sku() : ''
);
}
return $items;
}
/**
* 更新订单轨迹信息
*/
public function update_tracking_info($order_id) {
try {
$order = wc_get_order($order_id);
if (!$order) {
return;
}
$tracking_number = $order->get_meta('_czl_tracking_number');
if (empty($tracking_number)) {
return;
}
// 获取轨迹信息
$tracking_info = $this->api->get_tracking_info($tracking_number);
if (empty($tracking_info)) {
return;
}
// 保存轨迹信息
$order->update_meta_data('_czl_tracking_history', $tracking_info);
// 根据最新轨迹更新订单状态
$latest_status = end($tracking_info['trackDetails']);
if ($latest_status) {
// 检查是否已签收
if (strpos($latest_status['track_content'], '已签收') !== false ||
strpos($latest_status['track_content'], 'Delivered') !== false) {
$order->update_status('delivered', __('包裹已送达', 'woo-czl-express'));
} else {
$order->update_status('shipping', __('包裹运输中', 'woo-czl-express'));
}
}
$order->save();
} catch (Exception $e) {
error_log('CZL Express Error: Failed to update tracking info - ' . $e->getMessage());
}
}
}
}

View File

@ -0,0 +1,41 @@
<?php
class CZL_Product_Fields {
public function __construct() {
add_action('woocommerce_product_options_shipping', array($this, 'add_custom_fields'));
add_action('woocommerce_process_product_meta', array($this, 'save_custom_fields'));
}
/**
* 添加自定义字段
*/
public function add_custom_fields() {
echo '<div class="options_group">';
woocommerce_wp_text_input(array(
'id' => '_czl_name_en',
'label' => __('英文品名', 'woo-czl-express'),
'description' => __('用于国际物流申报', 'woo-czl-express'),
'desc_tip' => true,
));
woocommerce_wp_text_input(array(
'id' => '_czl_hs_code',
'label' => __('HS编码', 'woo-czl-express'),
'description' => __('海关商品编码', 'woo-czl-express'),
'desc_tip' => true,
));
echo '</div>';
}
/**
* 保存自定义字段
*/
public function save_custom_fields($post_id) {
$name_en = isset($_POST['_czl_name_en']) ? sanitize_text_field($_POST['_czl_name_en']) : '';
$hs_code = isset($_POST['_czl_hs_code']) ? sanitize_text_field($_POST['_czl_hs_code']) : '';
update_post_meta($post_id, '_czl_name_en', $name_en);
update_post_meta($post_id, '_czl_hs_code', $hs_code);
}
}

View File

@ -0,0 +1,378 @@
<?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_blue' => array(
'enabled' => true,
'groupName' => 'UPS Expedited',
'prefixes' => array('UPS 蓝单')
),
'ups_red' => 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' => 'European and American general package tax line',
'prefixes' => array(
'欧美经济专线(普货)',
'欧美标准专线(普货)',
'欧洲经济专线(普货)',
'欧洲标准专线(普货)'
)
),
'europe_b' => array(
'enabled' => true,
'groupName' => 'European and American B-class tax line',
'prefixes' => array(
'欧美经济专线(B类)',
'欧美标准专线(B类)',
'欧洲经济专线(B类)',
'欧洲标准专线(B类)'
)
),
'europe_battery' => array(
'enabled' => true,
'groupName' => 'European and American battery tax line',
'prefixes' => array(
'欧美经济专线(带电)',
'欧美标准专线(带电)',
'欧洲经济专线(带电)',
'欧洲标准专线(带电)'
)
),
'dubai_dhl' => array(
'enabled' => true,
'groupName' => 'Dubai DHL',
'prefixes' => array('迪拜DHL')
),
'dubai_ups' => array(
'enabled' => true,
'groupName' => 'Dubai UPS',
'prefixes' => array('迪拜UPS')
),
'dubai_fedex' => array(
'enabled' => true,
'groupName' => 'Dubai FEDEX',
'prefixes' => array('迪拜FEDEX')
),
'post' => array(
'enabled' => true,
'groupName' => 'Post',
'prefixes' => array('E特快', 'EMS')
),
'czl_uae' => array(
'enabled' => true,
'groupName' => 'CZL UAE 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;
}
error_log(sprintf(
'CZL Express: Adjusted rate from %f to %f using formula: %s',
$original_rate,
$rate,
$adjustment
));
return max(0, $rate);
} catch (Exception $e) {
error_log('CZL Express: Rate adjustment error - ' . $e->getMessage());
return $rate;
}
}
public function calculate_shipping_rate($package) {
try {
error_log('CZL Express: Calculating shipping rate for package: ' . print_r($package, true));
// 基本验证
if (empty($package['destination']['country'])) {
error_log('CZL Express: Empty destination country');
return array();
}
// 获取包裹信息
$weight = 0;
$length = 0;
$width = 0;
$height = 0;
foreach ($package['contents'] as $item) {
$product = $item['data'];
$quantity = $item['quantity'];
// 累加重量
$item_weight = (float)$product->get_weight();
if ($item_weight > 0) {
$weight += $item_weight * $quantity;
}
// 获取最大尺寸
$item_length = (float)$product->get_length();
$item_width = (float)$product->get_width();
$item_height = (float)$product->get_height();
$length = max($length, $item_length);
$width = max($width, $item_width);
$height = max($height, $item_height);
}
// 调用API获取运费
$api_params = array(
'weight' => $weight > 0 ? $weight : 0.1, // 默认最小重量0.1kg
'country' => $package['destination']['country'],
'postcode' => $package['destination']['postcode'],
'length' => $length > 0 ? $length : 1,
'width' => $width > 0 ? $width : 1,
'height' => $height > 0 ? $height : 1,
'cargoType' => 'P'
);
error_log('CZL Express: API params: ' . print_r($api_params, true));
// 调用API获取运费
$api_rates = $this->api->get_shipping_rate($api_params);
error_log('CZL Express: API response: ' . print_r($api_rates, true));
if (empty($api_rates)) {
return array();
}
// 按产品分组处理运费
$grouped_rates = array();
$all_rates = array(); // 存储所有线路
foreach ($api_rates as $rate) {
$product_name = $rate['product_name'];
$group_found = false;
// 调整运费
$adjusted_amount = $this->adjust_shipping_rate(floatval($rate['total_amount']));
// 添加单独的线路选项
$rate_id = sanitize_title($product_name);
$all_rates[$rate_id] = array(
'id' => 'czl_express_' . $rate_id,
'label' => sprintf(
'%s (%s)',
$product_name,
$this->translate_delivery_time($rate['product_aging'])
),
'cost' => $this->convert_currency($adjusted_amount),
'calc_tax' => 'per_order',
'meta_data' => array(
'product_id' => $rate['product_id'],
'delivery_time' => $rate['product_aging'],
'is_group' => false,
'original_amount' => $rate['total_amount'],
'adjusted_amount' => $adjusted_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]) ||
$rate['total_amount'] < $grouped_rates[$group_id]['meta_data']['original_amount']) {
$grouped_rates[$group_id] = array(
'id' => 'czl_express_group_' . $group_id,
'label' => sprintf(
'%s (%s)',
$group['groupName'],
$this->translate_delivery_time($rate['product_aging'])
),
'cost' => $this->convert_currency(floatval($rate['total_amount'])),
'calc_tax' => 'per_order',
'meta_data' => array(
'product_id' => $rate['product_id'],
'delivery_time' => $rate['product_aging'],
'original_name' => $product_name,
'is_group' => true,
'group_name' => $group['groupName'],
'original_amount' => $rate['total_amount']
)
);
}
$group_found = true;
break;
}
}
if ($group_found) break;
}
}
// 根据设置决定返回分组还是所有线路
$show_all_rates = get_option('czl_show_all_rates', 'no');
if ($show_all_rates === 'yes') {
return array_values($all_rates);
} else {
return array_values($grouped_rates);
}
} catch (Exception $e) {
error_log('CZL Express 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]);
}
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]);
}
// 其他常见格式的翻译
$translations = array(
'当天送达' => 'Same day delivery',
'次日送达' => 'Next day delivery',
'隔日送达' => 'Second day delivery',
'工作日' => 'working days',
'左右' => 'about'
);
foreach ($translations as $cn => $en) {
if (strpos($delivery_time, $cn) !== false) {
return $en;
}
}
// 如果没有匹配到任何格式,返回原始值
return $delivery_time;
}
}

View File

@ -0,0 +1,55 @@
<?php
class CZL_Settings {
private static $instance = null;
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
public function __construct() {
add_action('admin_init', array($this, 'register_settings'));
}
public function register_settings() {
register_setting('czl_options_group', 'czl_api_url');
register_setting('czl_options_group', 'czl_username');
register_setting('czl_options_group', 'czl_password');
register_setting('czl_options_group', 'czl_exchange_rate');
register_setting('czl_options_group', 'czl_product_groups');
}
public function get_settings_fields() {
return array(
'basic' => array(
array(
'name' => 'czl_api_url',
'label' => __('API URL', 'woo-czl-express'),
'type' => 'text',
'default' => '',
),
array(
'name' => 'czl_username',
'label' => __('Username', 'woo-czl-express'),
'type' => 'text',
'default' => '',
),
array(
'name' => 'czl_password',
'label' => __('Password', 'woo-czl-express'),
'type' => 'password',
'default' => '',
),
array(
'name' => 'czl_exchange_rate',
'label' => __('Exchange Rate', 'woo-czl-express'),
'type' => 'number',
'default' => '1',
'step' => '0.0001',
),
)
);
}
}

View File

@ -0,0 +1,161 @@
<?php
class WC_CZL_Shipping_Method extends WC_Shipping_Method {
private $api;
private $calculator;
public function __construct($instance_id = 0) {
parent::__construct($instance_id);
$this->id = 'czl_express';
$this->instance_id = absint($instance_id);
$this->title = __('CZL Express', 'woo-czl-express');
$this->method_title = __('CZL Express', 'woo-czl-express');
$this->method_description = __('CZL Express shipping integration', 'woo-czl-express');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->enabled = 'yes';
$this->init();
$this->api = new CZL_API();
$this->calculator = new CZL_Rate_Calculator();
// 添加前端资源
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
}
public function init() {
$this->init_form_fields();
$this->init_settings();
$this->title = $this->get_option('title', $this->method_title);
$this->enabled = $this->get_option('enabled', 'yes');
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
public function init_form_fields() {
$this->instance_form_fields = array(
'enabled' => array(
'title' => __('Enable/Disable', 'woo-czl-express'),
'type' => 'checkbox',
'label' => __('Enable this shipping method', 'woo-czl-express'),
'default' => 'yes'
),
'title' => array(
'title' => __('Method Title', 'woo-czl-express'),
'type' => 'text',
'description' => __('This controls the title which the user sees during checkout.', 'woo-czl-express'),
'default' => __('CZL Express', 'woo-czl-express'),
'desc_tip' => true
),
'show_all_rates' => array(
'title' => __('显示方式', 'woo-czl-express'),
'type' => 'select',
'description' => __('选择是显示分组运费还是显示所有具体线路', 'woo-czl-express'),
'default' => 'no',
'options' => array(
'no' => __('显示分组运费', 'woo-czl-express'),
'yes' => __('显示所有线路', 'woo-czl-express')
)
),
'sort_by' => array(
'title' => __('排序方式', 'woo-czl-express'),
'type' => 'select',
'description' => __('选择运费显示的排序方式', 'woo-czl-express'),
'default' => 'price',
'options' => array(
'price' => __('按价格排序', 'woo-czl-express'),
'time' => __('按时效排序', 'woo-czl-express')
)
),
'exchange_rate' => array(
'title' => __('汇率设置', 'woo-czl-express'),
'type' => 'text',
'description' => sprintf(
__('设置CNY到%s的汇率。例如如果1CNY=%s0.14输入0.14', 'woo-czl-express'),
get_woocommerce_currency(),
get_woocommerce_currency_symbol()
),
'default' => $this->get_default_exchange_rate(),
'desc_tip' => true
)
);
}
private function get_default_exchange_rate() {
$currency = get_woocommerce_currency();
$default_rates = array(
'USD' => 0.14,
'EUR' => 0.13,
'GBP' => 0.11,
// 添加其他常用货币...
);
return isset($default_rates[$currency]) ? $default_rates[$currency] : 1;
}
public function calculate_shipping($package = array()) {
if ($this->enabled !== 'yes') {
return;
}
try {
error_log('CZL Express: Starting shipping calculation');
$rates = $this->calculator->calculate_shipping_rate($package);
if (!empty($rates)) {
foreach ($rates as $rate) {
$rate_id = $this->id . '_' . $rate['product_id'];
$this->add_rate(array(
'id' => $rate_id,
'label' => $rate['method_title'],
'cost' => $rate['cost'],
'meta_data' => array(
'product_id' => $rate['product_id'],
'delivery_time' => $rate['delivery_time'],
'original_name' => $rate['original_name'],
'is_group' => $rate['is_group'],
'group_name' => $rate['group_name'],
'original_amount' => $rate['original_amount']
)
));
}
error_log('CZL Express: Added ' . count($rates) . ' shipping rates');
} else {
error_log('CZL Express: No shipping rates returned');
}
} catch (Exception $e) {
error_log('CZL Express Error: ' . $e->getMessage());
wc_add_notice($e->getMessage(), 'error');
}
}
public function enqueue_scripts() {
if (is_cart() || is_checkout()) {
wp_enqueue_style(
'czl-shipping-method',
WOO_CZL_EXPRESS_URL . 'assets/css/shipping-method.css',
array(),
WOO_CZL_EXPRESS_VERSION
);
}
}
public function process_admin_options() {
parent::process_admin_options();
// 保存汇率设置
$currency = get_woocommerce_currency();
$rate = $this->get_option('exchange_rate');
if (!empty($rate)) {
update_option('czl_exchange_rate_' . $currency, $rate);
}
}
}

View File

@ -0,0 +1,150 @@
<?php
class CZL_Tracking {
/**
* 在订单详情页显示跟踪信息
*/
public static function display_tracking_info($order) {
$tracking_number = get_post_meta($order->get_id(), '_czl_tracking_number', true);
if (!$tracking_number) {
return;
}
try {
$api = new CZL_API();
$tracking_info = $api->get_tracking($tracking_number);
if (!empty($tracking_info)) {
echo '<h2>' . __('物流跟踪信息', 'woo-czl-express') . '</h2>';
echo '<div class="czl-tracking-info">';
echo '<p><strong>' . __('运单号:', 'woo-czl-express') . '</strong>' . esc_html($tracking_number) . '</p>';
if (!empty($tracking_info['trackDetails'])) {
echo '<table class="czl-tracking-details">';
echo '<thead><tr>';
echo '<th>' . __('时间', 'woo-czl-express') . '</th>';
echo '<th>' . __('地点', 'woo-czl-express') . '</th>';
echo '<th>' . __('状态', 'woo-czl-express') . '</th>';
echo '</tr></thead><tbody>';
foreach ($tracking_info['trackDetails'] as $detail) {
echo '<tr>';
echo '<td>' . esc_html($detail['track_date']) . '</td>';
echo '<td>' . esc_html($detail['track_location']) . '</td>';
echo '<td>' . esc_html($detail['track_content']) . '</td>';
echo '</tr>';
}
echo '</tbody></table>';
}
echo '</div>';
}
} catch (Exception $e) {
error_log('CZL Express Tracking Error: ' . $e->getMessage());
}
}
/**
* 添加跟踪链接到订单邮件
*/
public static function add_tracking_to_email($order, $sent_to_admin = false) {
$tracking_number = get_post_meta($order->get_id(), '_czl_tracking_number', true);
if ($tracking_number) {
$tracking_url = 'https://exp.czl.net/track/?query=' . urlencode($tracking_number);
echo '<p><strong>' . __('物流跟踪:', 'woo-czl-express') . '</strong>';
echo '<a href="' . esc_url($tracking_url) . '" target="_blank">' . esc_html($tracking_number) . '</a></p>';
}
}
/**
* 在管理员订单页面显示轨迹信息
*/
public static function display_admin_tracking_info($order) {
$tracking_number = $order->get_meta('_czl_tracking_number');
if (empty($tracking_number)) {
return;
}
?>
<div class="czl-admin-tracking-info">
<h3><?php _e('CZL Express 运单信息', 'woo-czl-express'); ?></h3>
<p>
<?php
printf(
__('运单号: %s', 'woo-czl-express'),
'<a href="https://exp.czl.net/track/?query=' . esc_attr($tracking_number) . '" target="_blank">' .
esc_html($tracking_number) . '</a>'
);
?>
</p>
<?php
// 显示子单号
$child_numbers = $order->get_meta('_czl_child_numbers');
if (!empty($child_numbers)) {
echo '<p><strong>' . __('子单号:', 'woo-czl-express') . '</strong> ' .
implode(', ', array_map('esc_html', $child_numbers)) . '</p>';
}
// 显示参考号
$reference_number = $order->get_meta('_czl_reference_number');
if (!empty($reference_number)) {
echo '<p><strong>' . __('参考号:', 'woo-czl-express') . '</strong> ' .
esc_html($reference_number) . '</p>';
}
// 显示偏远信息
$is_remote = $order->get_meta('_czl_is_remote');
if (!empty($is_remote)) {
$remote_text = '';
switch ($is_remote) {
case 'Y':
$remote_text = __('偏远地区', 'woo-czl-express');
break;
case 'A':
$remote_text = __('FedEx偏远A级', 'woo-czl-express');
break;
case 'B':
$remote_text = __('FedEx偏远B级', 'woo-czl-express');
break;
case 'C':
$remote_text = __('FedEx偏远C级', 'woo-czl-express');
break;
case 'N':
$remote_text = __('非偏远地区', 'woo-czl-express');
break;
}
if ($remote_text) {
echo '<p><strong>' . __('地区类型:', 'woo-czl-express') . '</strong> ' .
esc_html($remote_text) . '</p>';
}
}
// 显示住宅信息
$is_residential = $order->get_meta('_czl_is_residential');
if ($is_residential === 'Y') {
echo '<p><strong>' . __('地址类型:', 'woo-czl-express') . '</strong> ' .
__('住宅地址', 'woo-czl-express') . '</p>';
}
// 显示轨迹信息
$tracking_history = $order->get_meta('_czl_tracking_history');
if (!empty($tracking_history['trackDetails'])) {
?>
<div class="czl-tracking-details">
<h4><?php _e('最新轨迹', 'woo-czl-express'); ?></h4>
<?php
$latest = reset($tracking_history['trackDetails']);
?>
<p>
<span class="tracking-date"><?php echo esc_html($latest['track_date']); ?></span>
<span class="tracking-content"><?php echo esc_html($latest['track_content']); ?></span>
</p>
</div>
<?php
}
?>
</div>
<?php
}
}

View File

@ -0,0 +1,22 @@
class WC_CZLExpress_Install {
public function migrate_to_hpos() {
global $wpdb;
// 获取所有需要迁移的订单
$orders = $wpdb->get_results("
SELECT post_id, meta_key, meta_value
FROM {$wpdb->postmeta}
WHERE meta_key LIKE '_czlexpress_%'
");
foreach ($orders as $order_data) {
$order = wc_get_order($order_data->post_id);
if ($order) {
// 迁移元数据到新系统
$order->update_meta_data($order_data->meta_key, $order_data->meta_value);
$order->save();
}
}
}
}

View File

@ -0,0 +1,27 @@
class WC_CZLExpress_Order {
public function save_tracking_info($order_id, $tracking_number, $label_url) {
// 使用新的HPOS API保存跟踪信息
$order = wc_get_order($order_id);
if ($order) {
// 使用新的元数据API
$order->update_meta_data('_czlexpress_tracking_number', $tracking_number);
$order->update_meta_data('_czlexpress_label_url', $label_url);
$order->save();
}
}
public function get_tracking_info($order_id) {
$order = wc_get_order($order_id);
if ($order) {
return array(
'tracking_number' => $order->get_meta('_czlexpress_tracking_number'),
'label_url' => $order->get_meta('_czlexpress_label_url')
);
}
return false;
}
}

View File

@ -0,0 +1,13 @@
class WC_CZLExpress_Orders_List {
public function get_orders($args = array()) {
// 使用新的订单查询API
$query = new \Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableQuery($args);
return $query->get_orders();
}
public function get_order_items($order_id) {
$order = wc_get_order($order_id);
return $order ? $order->get_items() : array();
}
}

View File

@ -0,0 +1,16 @@
class WC_CZLExpress_Shipping {
public function get_shipping_info($order_id) {
$order = wc_get_order($order_id);
if ($order) {
return array(
'weight' => $order->get_meta('_czlexpress_weight'),
'remote_fee' => $order->get_meta('_czlexpress_remote_fee'),
'shipping_method' => $order->get_meta('_czlexpress_shipping_method')
);
}
return false;
}
}

View File

@ -0,0 +1,631 @@
<?php
class WooCzlExpress {
private static $instance = null;
private $order_handler;
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
public function __construct() {
register_activation_hook(WOO_CZL_EXPRESS_PATH . 'woo-commerce-czlexpress.php', array('CZL_Install', 'init'));
$this->init();
add_action('admin_init', array($this, 'register_settings'));
add_action('wp_ajax_czl_test_connection', array($this, 'handle_test_connection'));
// 添加自定义订单状态
add_action('init', array($this, 'register_custom_order_statuses'));
add_filter('wc_order_statuses', array($this, 'add_custom_order_statuses'));
// 添加订单状态自动更新
add_action('wp_ajax_czl_update_order_status', array($this, 'update_order_status'));
add_action('wp_ajax_nopriv_czl_update_order_status', array($this, 'update_order_status'));
// 添加订单详情页面的轨迹显示
add_action('woocommerce_order_details_after_order_table', array('CZL_Tracking', 'display_tracking_info'));
add_action('woocommerce_admin_order_data_after_shipping_address', array('CZL_Tracking', 'display_admin_tracking_info'));
// 添加定时任务
add_action('czl_update_tracking_info', array($this, 'schedule_tracking_updates'));
// 注册定时任务
if (!wp_next_scheduled('czl_update_tracking_info')) {
wp_schedule_event(time(), 'hourly', 'czl_update_tracking_info');
}
}
private function init() {
// 加载依赖文件
$this->load_dependencies();
// 初始化产品字段
new CZL_Product_Fields();
// 初始化钩子
$this->init_hooks();
// 添加菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
}
private function load_dependencies() {
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-api.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-rate-calculator.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-shipping-method.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-install.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-settings.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-order-handler.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-order-data.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-label.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-tracking.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-product-fields.php';
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-order.php';
}
private function init_hooks() {
add_action('woocommerce_shipping_init', array($this, 'init_shipping_method'));
add_filter('woocommerce_shipping_methods', array($this, 'add_shipping_method'));
// 订单处理钩子
$order_handler = new CZL_Order_Handler();
add_action('woocommerce_order_status_processing', array($order_handler, 'create_shipment'));
add_action('woocommerce_order_status_cancelled', array($order_handler, 'cancel_shipment'));
// 运单标签钩子
add_filter('woocommerce_order_actions', array('CZL_Label', 'add_print_actions'), 10, 2);
add_action('wp_ajax_czl_print_label', array('CZL_Label', 'handle_print_request'));
// 跟踪信息显示钩子
add_action('woocommerce_order_details_after_order_table', array('CZL_Tracking', 'display_tracking_info'));
add_action('woocommerce_admin_order_data_after_shipping_address', array('CZL_Tracking', 'display_admin_tracking_info'));
// 添加订单操作
add_filter('woocommerce_order_actions', array($this, 'add_order_actions'), 10, 2);
// 添加AJAX处理
add_action('wp_ajax_czl_create_shipment', array($this, 'handle_create_shipment'));
add_action('wp_ajax_czl_bulk_create_shipment', array($this, 'handle_bulk_create_shipment'));
}
public function init_shipping_method() {
if (!class_exists('WC_CZL_Shipping_Method')) {
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-czl-shipping-method.php';
}
}
public function add_shipping_method($methods) {
if (class_exists('WC_CZL_Shipping_Method')) {
$methods['czl_express'] = 'WC_CZL_Shipping_Method';
}
return $methods;
}
public function add_admin_menu() {
add_menu_page(
__('CZL Express', 'woo-czl-express'),
__('CZL Express', 'woo-czl-express'),
'manage_woocommerce',
'czl-express',
array($this, 'render_settings_page'),
'dashicons-airplane'
);
// 添加订单管理子菜单
add_submenu_page(
'czl-express',
__('订单管理', 'woo-czl-express'),
__('订单管理', 'woo-czl-express'),
'manage_woocommerce',
'czl-express-orders',
array($this, 'render_orders_page')
);
add_submenu_page(
'czl-express',
__('基本设置', 'woo-czl-express'),
__('基本设置', 'woo-czl-express'),
'manage_options',
'czl-express'
);
add_submenu_page(
'czl-express',
__('产品分组', 'woo-czl-express'),
__('产品分组', 'woo-czl-express'),
'manage_options',
'czl-product-groups',
array($this, 'render_product_groups_page')
);
add_submenu_page(
'czl-express',
__('汇率设置', 'woo-czl-express'),
__('汇率设置', 'woo-czl-express'),
'manage_options',
'czl-exchange-rates',
array($this, 'render_exchange_rates_page')
);
}
public function render_product_groups_page() {
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
check_admin_referer('czl_save_product_groups', 'czl_product_groups_nonce')) {
$groups = array();
if (!empty($_POST['groups'])) {
foreach ($_POST['groups'] as $key => $group) {
if (empty($group['groupName'])) continue;
$prefixes = array_filter(array_map('trim', explode("\n", $group['prefixes'])));
if (empty($prefixes)) continue;
$groups[sanitize_key($group['groupName'])] = array(
'enabled' => !empty($group['enabled']),
'groupName' => sanitize_text_field($group['groupName']),
'prefixes' => array_map('sanitize_text_field', $prefixes)
);
}
}
update_option('czl_product_groups', $groups);
add_settings_error('czl_messages', 'czl_message', __('产品分组设置已保存', 'woo-czl-express'), 'updated');
}
// 显示设置页面
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/product-groups.php';
}
public function admin_page() {
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/admin-page.php';
}
/**
* 添加订单操作按钮
*/
public function add_order_actions($actions, $order) {
// 检查是否已创建运单
$tracking_number = get_post_meta($order->get_id(), '_czl_tracking_number', true);
if ($tracking_number) {
$actions['czl_cancel_shipment'] = __('取消CZL运单', 'woo-czl-express');
} else {
$actions['czl_create_shipment'] = __('创建CZL运单', 'woo-czl-express');
}
return $actions;
}
public function register_settings() {
// 注册设置组
register_setting(
'czl_options_group', // 设置组名称
'czl_username' // 用户名选项
);
register_setting(
'czl_options_group',
'czl_password'
);
register_setting(
'czl_options_group',
'czl_rate_adjustment'
);
// 添加设置分节
add_settings_section(
'czl_api_settings',
__('API设置', 'woo-czl-express'),
null,
'czl_options'
);
// 添加设置字段
add_settings_field(
'czl_username',
__('用户名', 'woo-czl-express'),
array($this, 'username_field_callback'),
'czl_options',
'czl_api_settings'
);
add_settings_field(
'czl_password',
__('密码', 'woo-czl-express'),
array($this, 'password_field_callback'),
'czl_options',
'czl_api_settings'
);
// 添加运费调整设置分节
add_settings_section(
'czl_rate_settings',
__('运费设置', 'woo-czl-express'),
null,
'czl_options'
);
add_settings_field(
'czl_rate_adjustment',
__('运费调整公式', 'woo-czl-express'),
array($this, 'rate_adjustment_field_callback'),
'czl_options',
'czl_rate_settings'
);
}
public function username_field_callback() {
$value = get_option('czl_username');
echo '<input type="text" name="czl_username" value="' . esc_attr($value) . '" />';
}
public function password_field_callback() {
$value = get_option('czl_password');
echo '<input type="password" name="czl_password" value="' . esc_attr($value) . '" />';
}
public function rate_adjustment_field_callback() {
$value = get_option('czl_rate_adjustment');
?>
<input type="text" name="czl_rate_adjustment" value="<?php echo esc_attr($value); ?>" class="regular-text">
<p class="description">
<?php _e('设置运费调整公式,支持百分比和固定金额。例如:', 'woo-czl-express'); ?><br>
- 10% + 10 (运费乘以1.1后加10)<br>
- 20% (运费乘以1.2)<br>
- +15 (运费加15)<br>
<?php _e('留空表示不调整运费', 'woo-czl-express'); ?>
</p>
<?php
}
public function handle_test_connection() {
check_ajax_referer('czl_test_api', 'nonce');
$username = get_option('czl_username');
$password = get_option('czl_password');
if (empty($username) || empty($password)) {
wp_send_json_error(array(
'message' => __('请先配置API账号和密码', 'woo-czl-express')
));
}
// 测试API连接
$response = wp_remote_get('https://tms-api-go.czl.net/api/countries', array(
'headers' => array(
'Authorization' => 'Basic ' . base64_encode($username . ':' . $password)
)
));
if (is_wp_error($response)) {
wp_send_json_error(array(
'message' => $response->get_error_message()
));
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (!empty($data) && isset($data['code']) && $data['code'] == 200) {
wp_send_json_success(array(
'message' => __('API连接测试成功', 'woo-czl-express')
));
} else {
wp_send_json_error(array(
'message' => __('API连接测试失败请检查账号密码是否正确', 'woo-czl-express')
));
}
}
public function render_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
// 显示设置页面
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/settings.php';
}
public function render_exchange_rates_page() {
// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
check_admin_referer('czl_save_exchange_rates', 'czl_exchange_rates_nonce')) {
// 删除所有现有汇率
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'czl_exchange_rate_%'");
// 保存新的汇率
if (!empty($_POST['rates'])) {
foreach ($_POST['rates'] as $data) {
if (empty($data['currency']) || !isset($data['rate'])) continue;
$currency = sanitize_text_field($data['currency']);
$rate = (float)$data['rate'];
if ($rate > 0) {
update_option('czl_exchange_rate_' . $currency, $rate);
}
}
}
add_settings_error('czl_messages', 'czl_message', __('汇率设置已保存', 'woo-czl-express'), 'updated');
}
// 显示设置页面
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/exchange-rates.php';
}
/**
* 注册自定义订单状态
*/
public function register_custom_order_statuses() {
register_post_status('wc-shipping', array(
'label' => __('运输中', 'woo-czl-express'),
'public' => true,
'show_in_admin_status_list' => true,
'show_in_admin_all_list' => true,
'exclude_from_search' => false,
'label_count' => _n_noop('运输中 <span class="count">(%s)</span>',
'运输中 <span class="count">(%s)</span>', 'woo-czl-express')
));
register_post_status('wc-delivered', array(
'label' => __('已送达', 'woo-czl-express'),
'public' => true,
'show_in_admin_status_list' => true,
'show_in_admin_all_list' => true,
'exclude_from_search' => false,
'label_count' => _n_noop('已送达 <span class="count">(%s)</span>',
'已送达 <span class="count">(%s)</span>', 'woo-czl-express')
));
}
/**
* 添加自定义订单状态到WooCommerce状态列表
*/
public function add_custom_order_statuses($order_statuses) {
$new_statuses = array(
'wc-shipping' => __('运输中', 'woo-czl-express'),
'wc-delivered' => __('已送达', 'woo-czl-express')
);
return array_merge($order_statuses, $new_statuses);
}
/**
* 在订单详情页显示轨迹信息
*/
public function display_tracking_info($order) {
$tracking_number = $order->get_meta('_czl_tracking_number');
if (empty($tracking_number)) {
return;
}
$tracking_history = $order->get_meta('_czl_tracking_history');
if (empty($tracking_history)) {
return;
}
?>
<h2><?php _e('物流轨迹', 'woo-czl-express'); ?></h2>
<div class="czl-tracking-info">
<p>
<?php
printf(
__('运单号: %s', 'woo-czl-express'),
'<a href="https://exp.czl.net/track/?query=' . esc_attr($tracking_number) . '" target="_blank">' .
esc_html($tracking_number) . '</a>'
);
?>
</p>
<ul class="czl-tracking-history">
<?php foreach ($tracking_history['trackDetails'] as $track): ?>
<li>
<span class="tracking-date"><?php echo esc_html($track['track_date']); ?></span>
<span class="tracking-content"><?php echo esc_html($track['track_content']); ?></span>
<?php if (!empty($track['track_location'])): ?>
<span class="tracking-location"><?php echo esc_html($track['track_location']); ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php
}
/**
* 定时更新所有运输中订单的轨迹
*/
public function schedule_tracking_updates() {
$orders = wc_get_orders(array(
'status' => array('shipping'),
'limit' => -1,
'meta_key' => '_czl_tracking_number',
'meta_compare' => 'EXISTS'
));
foreach ($orders as $order) {
$this->order_handler->update_tracking_info($order->get_id());
}
}
/**
* 在管理员订单页面显示轨迹信息
*/
public function display_admin_tracking_info($order) {
$tracking_number = $order->get_meta('_czl_tracking_number');
if (empty($tracking_number)) {
return;
}
?>
<div class="czl-admin-tracking-info">
<h3><?php _e('CZL Express 运单信息', 'woo-czl-express'); ?></h3>
<p>
<?php
printf(
__('运单号: %s', 'woo-czl-express'),
'<a href="https://exp.czl.net/track/?query=' . esc_attr($tracking_number) . '" target="_blank">' .
esc_html($tracking_number) . '</a>'
);
?>
</p>
</div>
<?php
}
public function handle_create_shipment() {
try {
// 验证nonce
check_ajax_referer('czl_create_shipment', 'nonce');
// 检查权限
if (!current_user_can('manage_woocommerce')) {
throw new Exception(__('权限不足', 'woo-czl-express'));
}
// 验证订单ID
$order_id = isset($_POST['order_id']) ? absint($_POST['order_id']) : 0;
if (!$order_id) {
throw new Exception(__('无效的订单ID', 'woo-czl-express'));
}
// 获取WooCommerce订单对象
$order = wc_get_order($order_id);
if (!$order) {
throw new Exception('订单不存在');
}
// 创建运单
$czl_order = new CZL_Order();
$result = $czl_order->create_shipment($order_id);
if (!empty($result['tracking_number'])) {
wp_send_json_success(array(
'message' => __('运单创建成功', 'woo-czl-express'),
'tracking_number' => $result['tracking_number']
));
} else {
throw new Exception('运单创建失败');
}
} catch (Exception $e) {
error_log('CZL Express Error: ' . $e->getMessage());
if (isset($order)) {
$order->add_order_note(
sprintf(
__('CZL Express运单创建失败: %s', 'woo-czl-express'),
$e->getMessage()
),
true
);
}
wp_send_json_error(array(
'message' => $e->getMessage() // 直接返回错误信息
));
}
}
public function handle_bulk_create_shipment() {
check_ajax_referer('czl_bulk_create_shipment', 'nonce');
if (!current_user_can('manage_woocommerce')) {
wp_send_json_error(array('message' => __('权限不足', 'woo-czl-express')));
}
$order_ids = isset($_POST['order_ids']) ? array_map('intval', $_POST['order_ids']) : array();
if (empty($order_ids)) {
wp_send_json_error(array('message' => __('请选择订单', 'woo-czl-express')));
}
$success = 0;
$failed = 0;
$failed_orders = array();
$czl_order = new CZL_Order();
foreach ($order_ids as $order_id) {
try {
$order = wc_get_order($order_id);
if (!$order) {
throw new Exception('订单不存在');
}
$result = $czl_order->create_shipment($order_id);
if (!empty($result)) {
// 更新WooCommerce订单元数据
$order->update_meta_data('_czl_tracking_number', $result['tracking_number']);
$order->update_meta_data('_czl_order_id', $result['order_id']);
// 保存子单号
if (!empty($result['childList'])) {
$child_numbers = array_map(function($child) {
return $child['child_number'];
}, $result['childList']);
$order->update_meta_data('_czl_child_numbers', $child_numbers);
}
// 添加订单备注
$order->add_order_note(
sprintf(
__('CZL Express运单创建成功。运单号: %s', 'woo-czl-express'),
$result['tracking_number']
),
true
);
// 更新订单状态为运输中
$order->update_status('shipping', __('运单已创建,包裹开始运输', 'woo-czl-express'));
// 保存更改
$order->save();
$success++;
} else {
throw new Exception('运单创建失败');
}
} catch (Exception $e) {
error_log('CZL Express Error: Failed to create shipment for order ' . $order_id . ' - ' . $e->getMessage());
if (isset($order)) {
$order->add_order_note(
sprintf(
__('CZL Express运单创建失败: %s', 'woo-czl-express'),
$e->getMessage()
),
true
);
}
$failed++;
$failed_orders[] = $order_id;
}
}
wp_send_json_success(array(
'message' => sprintf(
__('处理完成。成功:%d失败%d', 'woo-czl-express'),
$success,
$failed
),
'failed_orders' => $failed_orders
));
}
/**
* 渲染订单管理页面
*/
public function render_orders_page() {
// 检查权限
if (!current_user_can('manage_woocommerce')) {
wp_die(__('您没有足够的权限访问此页面', 'woo-czl-express'));
}
// 加载订单列表页面模板
require_once WOO_CZL_EXPRESS_PATH . 'admin/views/orders.php';
}
}

103
readme.md Normal file
View File

@ -0,0 +1,103 @@
# woocommerce-czlexpress
## 描述
1. 配置信息写入到mysql
2. order_id, tracking_number, label_url, 写入到mysql
3. 适配PHP7和PHP8
4. 功能嵌入到woocommerce内, 通过woocommerce的钩子函数, 实现功能
5. 查价运费是人民币, 支持按汇率转换为其他货币,主要是USD
## 系统要求
1. WordPress 5.0+
2. WooCommerce 6.0.0+
3. PHP 7.0+
4. MySQL 5.6+
## 特性
1. 完全支持WooCommerce高性能订单存储(HPOS)
2. 支持WooCommerce远程日志记录
3. 支持多语言
## 功能
1. 支持快捷查价, 然后映射产品和woocommerce的运输方式. 其中, 查价结果里, 有一些产品, 比如 "UPS 红单-TA价", "UPS 红单-DA55价", 这种以"UPS 红单"开头的产品. 还有比如: "FEDEX IE-DT价", "FEDEX IE-DE价", 这种以"FEDEX IE"开头的产品. 我希望在查价结果里, 把他们显示为一个类别, 比如"UPS 红单", 然后这一行只显示最便宜的一个"UPS 红单"开头的产品价格和时效. 然后这个类别可以点击展开, 下面一个列表显示所有以"UPS 红单"开头的具体产品和相关信息. 并且可以自定义类别名称和产品开头, 例如这个配置:
productGroupConfig: {
'UPS 蓝单': {
prefixes: ['UPS 蓝单'],
groupName: 'UPS 蓝单'
},
'UPS 红单': {
prefixes: ['UPS 红单'],
groupName: 'UPS 红单'
},
"江苏FEDEX IE": {
prefixes: ["江苏FEDEX IE"],
groupName: "江苏FEDEX IE"
},
"江苏FEDEX IP": {
prefixes: ["江苏FEDEX IP"],
groupName: "江苏FEDEX IP"
},
'FEDEX IE': {
prefixes: ['FEDEX IE'],
groupName: 'FEDEX IE'
},
'FEDEX IP': {
prefixes: ['FEDEX IP'],
groupName: 'FEDEX IP'
},
"DHL": {
prefixes: ["DHL"],
groupName: "DHL"
},
"欧美普货包税专线": {
prefixes: ["欧美经济专线(普货)","欧美标准专线(普货)","欧洲经济专线(普货)","欧洲标准专线(普货)"],
groupName: "欧美普货包税专线"
},
"欧美B类包税专线": {
prefixes: ["欧美经济专线(B类)","欧美标准专线(B类)","欧洲经济专线(B类)","欧洲标准专线(B类)"],
groupName: "欧美B类包税专线"
},
"欧美带电包税专线": {
prefixes: ["欧美经济专线(带电)","欧美标准专线(带电)","欧洲经济专线(带电)","欧洲标准专线(带电)"],
groupName: "欧美带电包税专线"
},
"迪拜DHL": {
prefixes: ["迪拜DHL"],
groupName: "迪拜DHL"
},
"迪拜UPS": {
prefixes: ["迪拜UPS"],
groupName: "迪拜UPS"
},
"迪拜FEDEX": {
prefixes: ["迪拜FEDEX"],
groupName: "迪拜FEDEX"
},
"邮政": {
prefixes: ["E特快","EMS"],
groupName: "邮政"
},
"CZL阿联酋专线": {
prefixes: ["CZL阿联酋"],
groupName: "CZL阿联酋专线"
},
}
用户可以选择按产品分组映射woocommerce的运输方式. 也可以选择具体产品映射woocommerce的运输方式.
2. 设置运输价格时, 可以设置在CZLExpress的运费上额外加上一定比例和固定金额, 支持表达式, 例如"10% + 10", 那么就是CZLExpress的运费乘以1.1, 然后加上10元.
3. 支持设置账号密码, 然后在woocommerce订单列表, 支持快捷下单到CZLExpress, 如果下单无误, 支持打印标签,如果下单有问题, 显示报错内容, 可以手动修改, 然后打印标签.
4. 在woocommerce的订单详情页, 显示CZLExpress的订单跟踪号, 并且可以点击跟踪号, 跳转到CZLExpress的订单跟踪页面.
5. 在woocommerce的订单详情页, 显示CZLExpress的订单标签, 并且可以点击标签, 跳转到CZLExpress的订单标签下载页面.
6. 可以把订单运输轨迹, 显示在woocommerce的订单详情页.
7. 当用户在woocommerce下单时, 自动查询偏远和偏远费用, 然后加到运输费用里. 并且也单独显示偏远和偏远费用.

View File

@ -0,0 +1,56 @@
<?php
/**
* 运费方式分组显示模板
*/
defined('ABSPATH') || exit;
?>
<tr class="shipping-method-group">
<td>
<input type="radio"
name="shipping_method[<?php echo $index; ?>]"
data-index="<?php echo $index; ?>"
value="<?php echo esc_attr($rate->id); ?>"
class="shipping_method"
<?php checked($rate->id, $chosen_method); ?> />
</td>
<td>
<label for="shipping_method_<?php echo $index; ?>">
<?php echo wp_kses_post($rate->label); ?>
<?php if (!empty($rate->has_sub_methods)): ?>
<span class="toggle-sub-methods dashicons dashicons-arrow-down-alt2"></span>
<?php endif; ?>
</label>
<?php if ($rate->remote_fee > 0): ?>
<div class="remote-fee-notice">
<?php printf(__('Remote Area Fee: %s', 'woo-czl-express'),
wc_price($rate->remote_fee)); ?>
</div>
<?php endif; ?>
<?php if (!empty($rate->has_sub_methods)): ?>
<div class="sub-methods" style="display: none;">
<table class="sub-methods-table">
<thead>
<tr>
<th><?php _e('Method', 'woo-czl-express'); ?></th>
<th><?php _e('Delivery Time', 'woo-czl-express'); ?></th>
<th><?php _e('Cost', 'woo-czl-express'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($rate->sub_methods as $sub_method): ?>
<tr>
<td><?php echo esc_html($sub_method['method_title']); ?></td>
<td><?php echo esc_html($sub_method['delivery_time']); ?></td>
<td><?php echo wc_price($sub_method['cost']); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</td>
<td><?php echo wc_price($rate->cost); ?></td>
</tr>

View File

@ -0,0 +1,65 @@
<?php
/**
* Plugin Name: WooCommerce CZL Express Shipping
* Plugin URI: https://your-domain.com/
* Description: CZL Express shipping integration for WooCommerce
* Version: 1.0.0
* Author: Your Name
* Author URI: https://your-domain.com/
* Text Domain: woo-czl-express
* Domain Path: /languages
* Requires PHP: 7.0
* WC requires at least: 6.0.0
* WC tested up to: 8.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('WOO_CZL_EXPRESS_VERSION', '1.0.0');
define('WOO_CZL_EXPRESS_PATH', plugin_dir_path(__FILE__));
define('WOO_CZL_EXPRESS_URL', plugin_dir_url(__FILE__));
// 声明支持HPOS
add_action('before_woocommerce_init', function() {
if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
}
});
// 检查环境
function wc_czlexpress_check_environment() {
if (!class_exists('WooCommerce')) {
add_action('admin_notices', function() {
echo '<div class="error"><p>' .
__('CZL Express requires WooCommerce to be installed and active', 'woocommerce-czlexpress') .
'</p></div>';
});
return false;
}
if (version_compare(WC_VERSION, '6.0.0', '<')) {
add_action('admin_notices', function() {
echo '<div class="error"><p>' .
__('CZL Express requires WooCommerce 6.0.0 or higher', 'woocommerce-czlexpress') .
'</p></div>';
});
return false;
}
return true;
}
// 初始化插件
function woo_czl_express_init() {
if (wc_czlexpress_check_environment()) {
require_once WOO_CZL_EXPRESS_PATH . 'includes/class-woo-czl-express.php';
WooCzlExpress::instance();
}
}
add_action('plugins_loaded', 'woo_czl_express_init');
add_action('woocommerce_order_status_processing', array('CZL_Order', 'create_shipment'));