mirror of
https://github.com/woodchen-ink/czlexpress-for-woocommerce.git
synced 2025-07-18 05:51:57 +08:00
- Updated AJAX function to validate order ID instead of tracking number for better error handling. - Enhanced success and error responses to include detailed messages for improved user feedback. - Refactored API request methods for better structure and error handling. - Added functionality to automatically update order status and add notes based on tracking information. - Improved logging for API requests and responses to aid in debugging. These changes enhance the reliability and user experience of the CZL Express plugin.
500 lines
16 KiB
PHP
500 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* Plugin Name: CZL Express for WooCommerce
|
|
* Plugin URI: https://github.com/woodchen-ink/czlexpress-for-woocommerce
|
|
* Description: CZL Express shipping integration for WooCommerce. Provides real-time shipping rates, shipment creation, and package tracking for CZL Express delivery service.
|
|
* Version: 1.0.0
|
|
* Requires at least: 6.7
|
|
* Requires PHP: 8.0
|
|
* Author: CZL Express
|
|
* Author URI: https://exp.czl.net
|
|
* License: GPL v2 or later
|
|
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
|
* Text Domain: czlexpress-for-woocommerce
|
|
* Domain Path: /languages
|
|
* WC requires at least: 6.0.0
|
|
* WC tested up to: 8.3.0
|
|
*
|
|
* This plugin is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* any later version.
|
|
*
|
|
* This plugin is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this plugin. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
|
|
*/
|
|
|
|
// 防止直接访问
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
// 防止重复加载
|
|
if (defined('CZL_EXPRESS_VERSION')) {
|
|
return;
|
|
}
|
|
|
|
// 定义插件常量
|
|
define('CZL_EXPRESS_VERSION', '1.0.0');
|
|
define('CZL_EXPRESS_PATH', plugin_dir_path(__FILE__));
|
|
define('CZL_EXPRESS_URL', plugin_dir_url(__FILE__));
|
|
|
|
// 加载核心类文件
|
|
require_once CZL_EXPRESS_PATH . 'includes/class-czl-logger.php';
|
|
|
|
// 检查环境
|
|
function czl_express_check_environment() {
|
|
// 检查WooCommerce是否已安装并激活
|
|
if (!class_exists('WC_Shipping_Method')) {
|
|
add_action('admin_notices', function() {
|
|
echo '<div class="error"><p>' .
|
|
esc_html__('CZL Express requires WooCommerce to be installed and activated', 'czlexpress-for-woocommerce') .
|
|
'</p></div>';
|
|
});
|
|
return false;
|
|
}
|
|
|
|
// 检查WooCommerce版本
|
|
if (version_compare(WC_VERSION, '6.0.0', '<')) {
|
|
add_action('admin_notices', function() {
|
|
echo '<div class="error"><p>' .
|
|
esc_html__('CZL Express requires WooCommerce 6.0.0 or higher', 'czlexpress-for-woocommerce') .
|
|
'</p></div>';
|
|
});
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// 在插件激活时创建数据表
|
|
register_activation_hook(__FILE__, 'czl_express_activate');
|
|
|
|
function czl_express_activate() {
|
|
// 确保加载安装类
|
|
require_once CZL_EXPRESS_PATH . 'includes/class-czl-install.php';
|
|
|
|
// 创建数据表和默认选项
|
|
CZL_Install::init();
|
|
|
|
// 记录版本号
|
|
update_option('czl_express_version', CZL_EXPRESS_VERSION);
|
|
}
|
|
|
|
// 在插件停用时清理
|
|
register_deactivation_hook(__FILE__, function() {
|
|
// 清理定时任务
|
|
wp_clear_scheduled_hook('czl_update_tracking_info');
|
|
});
|
|
|
|
// 声明支持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 czl_express_init() {
|
|
if (czl_express_check_environment()) {
|
|
CZL_Logger::info('Plugin initialization started');
|
|
|
|
// 加载必要的类文件
|
|
require_once CZL_EXPRESS_PATH . 'includes/class-czlexpress.php';
|
|
require_once CZL_EXPRESS_PATH . 'includes/class-czl-api.php';
|
|
require_once CZL_EXPRESS_PATH . 'includes/class-czl-order.php';
|
|
require_once CZL_EXPRESS_PATH . 'includes/class-czl-ajax.php';
|
|
|
|
CZLExpress::instance();
|
|
|
|
// 初始化AJAX处理器
|
|
new CZL_Ajax();
|
|
|
|
// 添加AJAX处理
|
|
add_action('wp_ajax_czl_create_shipment', 'czl_ajax_create_shipment');
|
|
add_action('wp_ajax_czl_update_tracking_number', 'czl_ajax_update_tracking_number');
|
|
add_action('wp_ajax_czl_update_tracking_info', 'czl_ajax_update_tracking_info');
|
|
|
|
// 添加前端脚本
|
|
add_action('admin_enqueue_scripts', 'czl_enqueue_admin_scripts');
|
|
|
|
// 注册自定义订单状态
|
|
add_action('init', 'register_czl_order_statuses');
|
|
|
|
// 在init钩子中加载翻译文件
|
|
add_action('init', function() {
|
|
load_plugin_textdomain(
|
|
'czlexpress-for-woocommerce',
|
|
false,
|
|
dirname(plugin_basename(__FILE__)) . '/languages'
|
|
);
|
|
});
|
|
|
|
CZL_Logger::info('Plugin initialization completed');
|
|
}
|
|
}
|
|
|
|
// 注册自定义订单状态
|
|
function register_czl_order_statuses() {
|
|
register_post_status('wc-in_transit', array(
|
|
'label' => _x('In Transit', 'Order status', 'czlexpress-for-woocommerce'),
|
|
'public' => true,
|
|
'show_in_admin_status_list' => true,
|
|
'show_in_admin_all_list' => true,
|
|
'exclude_from_search' => false,
|
|
/* translators: %s: number of orders */
|
|
'label_count' => _n_noop(
|
|
'In Transit <span class="count">(%s)</span>',
|
|
'In Transit <span class="count">(%s)</span>',
|
|
'czlexpress-for-woocommerce'
|
|
)
|
|
));
|
|
|
|
// 添加到WooCommerce订单状态列表
|
|
add_filter('wc_order_statuses', function($order_statuses) {
|
|
$new_statuses = array();
|
|
|
|
// 在processing后面插入新状态
|
|
foreach ($order_statuses as $key => $status) {
|
|
$new_statuses[$key] = $status;
|
|
if ($key === 'wc-processing') {
|
|
$new_statuses['wc-in_transit'] = _x('In Transit', 'Order status', 'czlexpress-for-woocommerce');
|
|
}
|
|
}
|
|
|
|
return $new_statuses;
|
|
});
|
|
}
|
|
|
|
// 注册前端脚本
|
|
function czl_enqueue_admin_scripts($hook) {
|
|
// 在WooCommerce订单页面和CZL Express订单页面加载脚本
|
|
if (!in_array($hook, array('woocommerce_page_wc-orders', 'toplevel_page_czl-express-orders'))) {
|
|
return;
|
|
}
|
|
|
|
// 加载 Thickbox
|
|
add_thickbox();
|
|
|
|
// 加载自定义脚本
|
|
wp_enqueue_script(
|
|
'czl-admin-script',
|
|
CZL_EXPRESS_URL . 'assets/js/admin.js',
|
|
array('jquery'),
|
|
CZL_EXPRESS_VERSION,
|
|
true
|
|
);
|
|
|
|
wp_localize_script('czl-admin-script', 'czl_ajax', array(
|
|
'ajax_url' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('czl_ajax_nonce'),
|
|
'creating_text' => 'Creating shipment...',
|
|
'success_text' => 'Shipment created successfully',
|
|
'error_text' => 'Failed to create shipment'
|
|
));
|
|
}
|
|
|
|
// AJAX处理函数
|
|
function czl_ajax_create_shipment() {
|
|
check_ajax_referer('czl_ajax_nonce', 'nonce');
|
|
|
|
if (!current_user_can('edit_shop_orders')) {
|
|
wp_send_json_error('Permission denied');
|
|
}
|
|
|
|
$order_id = isset($_POST['order_id']) ? intval($_POST['order_id']) : 0;
|
|
if (!$order_id) {
|
|
wp_send_json_error('Invalid order ID');
|
|
}
|
|
|
|
try {
|
|
$czl_order = new CZL_Order();
|
|
$result = $czl_order->create_shipment($order_id);
|
|
|
|
if ($result) {
|
|
wp_send_json_success(array(
|
|
'message' => 'Shipment created successfully'
|
|
));
|
|
} else {
|
|
wp_send_json_error('Failed to create shipment');
|
|
}
|
|
} catch (Exception $e) {
|
|
wp_send_json_error($e->getMessage());
|
|
}
|
|
}
|
|
|
|
// 更新跟踪单号的AJAX处理函数
|
|
function czl_ajax_update_tracking_number() {
|
|
check_ajax_referer('czl_ajax_nonce', 'nonce');
|
|
|
|
if (!current_user_can('edit_shop_orders')) {
|
|
wp_send_json_error(array('message' => '权限不足'));
|
|
}
|
|
|
|
$order_id = isset($_POST['order_id']) ? intval($_POST['order_id']) : 0;
|
|
$tracking_number = isset($_POST['tracking_number']) ? sanitize_text_field($_POST['tracking_number']) : '';
|
|
|
|
if (!$order_id || !$tracking_number) {
|
|
wp_send_json_error(array('message' => '参数无效'));
|
|
}
|
|
|
|
try {
|
|
global $wpdb;
|
|
|
|
// 获取运单信息
|
|
$shipment = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM {$wpdb->prefix}czl_shipments WHERE order_id = %d",
|
|
$order_id
|
|
));
|
|
|
|
if (!$shipment) {
|
|
throw new Exception('运单不存在');
|
|
}
|
|
|
|
// 更新数据库中的跟踪单号
|
|
$updated = $wpdb->update(
|
|
$wpdb->prefix . 'czl_shipments',
|
|
array('tracking_number' => $tracking_number),
|
|
array('order_id' => $order_id),
|
|
array('%s'),
|
|
array('%d')
|
|
);
|
|
|
|
if ($updated === false) {
|
|
throw new Exception('数据库更新失败');
|
|
}
|
|
|
|
// 更新订单元数据
|
|
$order = wc_get_order($order_id);
|
|
if ($order) {
|
|
$order->update_meta_data('_czl_tracking_number', $tracking_number);
|
|
$order->save();
|
|
|
|
// 添加订单备注
|
|
$order->add_order_note(sprintf(
|
|
'运单号已更新为: %s',
|
|
$tracking_number
|
|
));
|
|
}
|
|
|
|
// 清除相关缓存
|
|
$tracking = new CZL_Tracking();
|
|
$tracking->clear_tracking_cache($shipment->id, $tracking_number, $order_id);
|
|
|
|
wp_send_json_success(array(
|
|
'message' => '运单号更新成功',
|
|
'tracking_number' => $tracking_number
|
|
));
|
|
|
|
} catch (Exception $e) {
|
|
wp_send_json_error(array('message' => $e->getMessage()));
|
|
}
|
|
}
|
|
|
|
// 更新轨迹信息的AJAX处理函数
|
|
function czl_ajax_update_tracking_info() {
|
|
try {
|
|
// 验证nonce
|
|
check_ajax_referer('czl_ajax_nonce', 'nonce');
|
|
|
|
// 验证权限
|
|
if (!current_user_can('edit_shop_orders')) {
|
|
throw new Exception('权限不足');
|
|
}
|
|
|
|
$order_id = isset($_POST['order_id']) ? intval($_POST['order_id']) : 0;
|
|
if (!$order_id) {
|
|
throw new Exception('订单ID不能为空');
|
|
}
|
|
|
|
// 获取运单信息
|
|
global $wpdb;
|
|
$shipment = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT tracking_number FROM {$wpdb->prefix}czl_shipments WHERE order_id = %d",
|
|
$order_id
|
|
));
|
|
|
|
if (!$shipment || empty($shipment->tracking_number)) {
|
|
throw new Exception('未找到运单信息');
|
|
}
|
|
|
|
$tracking = new CZL_Tracking();
|
|
$result = $tracking->update_tracking_info($shipment->tracking_number);
|
|
|
|
if ($result['success']) {
|
|
wp_send_json_success(array(
|
|
'message' => '轨迹更新成功',
|
|
'data' => $result['data']
|
|
));
|
|
} else {
|
|
throw new Exception($result['message'] ?: '更新失败');
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
CZL_Logger::error('Error updating tracking info', array('error' => $e->getMessage()));
|
|
wp_send_json_error(array('message' => $e->getMessage()));
|
|
}
|
|
}
|
|
|
|
// 异步处理轨迹更新
|
|
function czl_do_update_tracking_info($order_id) {
|
|
try {
|
|
$czl_order = new CZL_Order();
|
|
$czl_order->update_tracking_info($order_id);
|
|
} catch (Exception $e) {
|
|
CZL_Logger::error('Error updating tracking info', array(
|
|
'order_id' => $order_id,
|
|
'error' => $e->getMessage()
|
|
));
|
|
}
|
|
}
|
|
add_action('czl_do_update_tracking_info', 'czl_do_update_tracking_info');
|
|
|
|
add_action('plugins_loaded', 'czl_express_init');
|
|
|
|
// 自动创建运单的钩子
|
|
add_action('woocommerce_order_status_processing', function($order_id) {
|
|
$czl_order = new CZL_Order();
|
|
try {
|
|
$czl_order->create_shipment($order_id);
|
|
} catch (Exception $e) {
|
|
CZL_Logger::error('Auto create shipment failed', array(
|
|
'order_id' => $order_id,
|
|
'error' => $e->getMessage()
|
|
));
|
|
}
|
|
});
|
|
|
|
// 添加自定义Cron间隔
|
|
add_filter('cron_schedules', 'czl_add_cron_interval');
|
|
function czl_add_cron_interval($schedules) {
|
|
// 添加每30分钟执行一次的间隔
|
|
$schedules['czl_thirty_minutes'] = array(
|
|
'interval' => 1800, // 30分钟 = 1800秒
|
|
'display' => esc_html__('Every 30 minutes', 'czlexpress-for-woocommerce')
|
|
);
|
|
return $schedules;
|
|
}
|
|
|
|
// 添加定时任务处理函数
|
|
function czl_sync_tracking_numbers() {
|
|
global $wpdb;
|
|
|
|
// 获取最后同步时间
|
|
$last_sync = get_option('czl_last_tracking_sync', 0);
|
|
$current_time = time();
|
|
|
|
// 如果距离上次同步不到25分钟,则跳过
|
|
if (($current_time - $last_sync) < 1500) {
|
|
return;
|
|
}
|
|
|
|
// 限制每次处理的订单数量
|
|
$limit = 10;
|
|
|
|
// 获取需要同步的订单
|
|
$shipments = $wpdb->get_results($wpdb->prepare("
|
|
SELECT order_id, tracking_number, czl_order_id
|
|
FROM {$wpdb->prefix}czl_shipments
|
|
WHERE czl_order_id IS NOT NULL
|
|
AND (last_sync_time IS NULL OR last_sync_time < DATE_SUB(NOW(), INTERVAL 25 MINUTE))
|
|
LIMIT %d
|
|
", $limit));
|
|
|
|
if (empty($shipments)) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$api = new CZL_API();
|
|
|
|
foreach ($shipments as $shipment) {
|
|
// 为每个订单安排单独的更新事件
|
|
wp_schedule_single_event(
|
|
time() + rand(1, 300), // 随机延迟1-300秒
|
|
'czl_do_sync_single_tracking',
|
|
array($shipment->order_id, $shipment->tracking_number, $shipment->czl_order_id)
|
|
);
|
|
}
|
|
|
|
// 更新最后同步时间
|
|
update_option('czl_last_tracking_sync', $current_time);
|
|
|
|
CZL_Logger::info('Tracking sync scheduled', array(
|
|
'shipment_count' => count($shipments)
|
|
));
|
|
|
|
} catch (Exception $e) {
|
|
CZL_Logger::error('Tracking number sync failed', array(
|
|
'error' => $e->getMessage()
|
|
));
|
|
}
|
|
}
|
|
|
|
// 处理单个订单的同步
|
|
function czl_do_sync_single_tracking($order_id, $current_tracking, $czl_order_id) {
|
|
try {
|
|
global $wpdb;
|
|
$api = new CZL_API();
|
|
|
|
// 获取最新的跟踪单号
|
|
$response = $api->get_tracking_number($czl_order_id);
|
|
|
|
// 更新最后同步时间
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'czl_shipments',
|
|
array('last_sync_time' => current_time('mysql')),
|
|
array('order_id' => $order_id),
|
|
array('%s'),
|
|
array('%d')
|
|
);
|
|
|
|
if (!empty($response['tracking_number']) && $response['tracking_number'] !== $current_tracking) {
|
|
// 更新跟踪单号
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'czl_shipments',
|
|
array('tracking_number' => $response['tracking_number']),
|
|
array('order_id' => $order_id),
|
|
array('%s'),
|
|
array('%d')
|
|
);
|
|
|
|
// 更新订单信息
|
|
$order = wc_get_order($order_id);
|
|
if ($order) {
|
|
$order->update_meta_data('_czl_tracking_number', $response['tracking_number']);
|
|
$order->save();
|
|
|
|
$tracking_link = sprintf(
|
|
'<a href="https://exp.czl.net/track/?query=%s" target="_blank">%s</a>',
|
|
$response['tracking_number'],
|
|
__('查看物流', 'czlexpress-for-woocommerce')
|
|
);
|
|
|
|
$order->add_order_note(
|
|
sprintf(
|
|
/* translators: 1: tracking number 2: tracking link */
|
|
__('运单号已更新为: %1$s\n%2$s', 'czlexpress-for-woocommerce'),
|
|
$response['tracking_number'],
|
|
$tracking_link
|
|
),
|
|
true
|
|
);
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
CZL_Logger::error('Failed to sync tracking number', array(
|
|
'order_id' => $order_id,
|
|
'error' => $e->getMessage()
|
|
));
|
|
}
|
|
}
|
|
add_action('czl_do_sync_single_tracking', 'czl_do_sync_single_tracking', 10, 3);
|
|
|
|
// 注册定时任务钩子
|
|
add_action('czl_sync_tracking_numbers_hook', 'czl_sync_tracking_numbers'); |