文章目录
-
- 在当今竞争激烈的电商环境中,完善的售后与退货处理系统不仅是提升客户满意度的关键,更是企业运营效率的重要保障。根据Statista的数据显示,超过60%的在线购物者在购买前会查看退货政策,而高效的退货处理可以将客户保留率提高30%以上。 对于使用WordPress构建电商网站的开发者和企业来说,构建一个稳定、高效的售后与退货处理接口尤为重要。WordPress作为全球最流行的内容管理系统,配合WooCommerce等电商插件,为开发者提供了强大的基础框架。然而,标准解决方案往往无法满足企业的个性化需求,这就需要我们深入代码层面,构建定制化的售后接口。 本文将基于WordPress开源系统,为行业新人和程序员提供三种周全的售后与退货处理接口构建方法,从基础到高级,逐步深入。
-
- WooCommerce作为WordPress最流行的电商插件,已经内置了基本的订单管理和退款功能。要构建售后接口,首先需要理解其核心数据结构: // WooCommerce订单退款相关的主要数据表 // wp_woocommerce_order_items - 订单项目 // wp_woocommerce_order_itemmeta - 订单项目元数据 // wp_posts (post_type = 'shop_order') - 订单主数据 // wp_postmeta - 订单元数据 // wp_comments (comment_type = 'order_note') - 订单备注/历史 // 退款创建的基本流程 function create_woocommerce_refund($order_id, $amount, $reason) { $order = wc_get_order($order_id); // 创建退款对象 $refund = wc_create_refund(array( 'amount' => $amount, 'reason' => $reason, 'order_id' => $order_id, 'refund_payment' => true, // 是否退款到支付方式 'restock_items' => true // 是否恢复库存 )); return $refund; }
- 基于WooCommerce原生功能,我们可以构建一个基础的售后请求处理接口: /** * 售后请求处理类 */ class Basic_After_Sales_Handler { private $request_types = array('refund', 'return', 'exchange', 'repair'); /** * 初始化售后系统 */ public function init() { // 注册售后请求自定义帖子类型 add_action('init', array($this, 'register_after_sales_post_type')); // 添加REST API端点 add_action('rest_api_init', array($this, 'register_rest_endpoints')); // 添加管理界面 add_action('admin_menu', array($this, 'add_admin_menu')); } /** * 注册售后请求自定义帖子类型 */ public function register_after_sales_post_type() { $labels = array( 'name' => '售后请求', 'singular_name' => '售后请求', 'menu_name' => '售后管理', 'add_new' => '添加请求', 'add_new_item' => '添加新售后请求', 'edit_item' => '编辑售后请求', 'new_item' => '新售后请求', 'view_item' => '查看售后请求', 'search_items' => '搜索售后请求', 'not_found' => '未找到售后请求', 'not_found_in_trash' => '回收站中无售后请求' ); $args = array( 'labels' => $labels, 'public' => false, 'publicly_queryable' => false, 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, 'rewrite' => array('slug' => 'after-sales'), 'capability_type' => 'post', 'has_archive' => false, 'hierarchical' => false, 'menu_position' => 56, 'menu_icon' => 'dashicons-undo', 'supports' => array('title', 'editor', 'author', 'custom-fields'), 'show_in_rest' => true // 启用REST API支持 ); register_post_type('after_sales_request', $args); // 注册售后状态分类 register_taxonomy( 'after_sales_status', 'after_sales_request', array( 'label' => '售后状态', 'rewrite' => array('slug' => 'after-sales-status'), 'hierarchical' => true, 'show_in_rest' => true ) ); } /** * 注册REST API端点 */ public function register_rest_endpoints() { // 创建售后请求端点 register_rest_route('after-sales/v1', '/request', array( 'methods' => 'POST', 'callback' => array($this, 'handle_after_sales_request'), 'permission_callback' => array($this, 'check_request_permission'), 'args' => array( 'order_id' => array( 'required' => true, 'validate_callback' => function($param) { return is_numeric($param) && $param > 0; } ), 'request_type' => array( 'required' => true, 'validate_callback' => function($param) { $allowed_types = array('refund', 'return', 'exchange', 'repair'); return in_array($param, $allowed_types); } ), 'reason' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ), 'items' => array( 'required' => false, 'validate_callback' => function($param) { return is_array($param); } ) ) )); // 获取售后请求状态端点 register_rest_route('after-sales/v1', '/status/(?P<id>d+)', array( 'methods' => 'GET', 'callback' => array($this, 'get_request_status'), 'permission_callback' => function() { return current_user_can('read'); } )); } /** * 处理售后请求 */ public function handle_after_sales_request($request) { $parameters = $request->get_params(); // 验证订单是否存在且属于当前用户 $order = wc_get_order($parameters['order_id']); if (!$order) { return new WP_Error('invalid_order', '订单不存在', array('status' => 404)); } // 检查用户权限 $user_id = get_current_user_id(); if ($order->get_customer_id() !== $user_id && !current_user_can('manage_woocommerce')) { return new WP_Error('permission_denied', '无权访问此订单', array('status' => 403)); } // 创建售后请求记录 $request_id = wp_insert_post(array( 'post_title' => sprintf('售后请求 - 订单 #%s', $parameters['order_id']), 'post_content' => $parameters['reason'], 'post_status' => 'publish', 'post_type' => 'after_sales_request', 'post_author' => $user_id, 'meta_input' => array( '_order_id' => $parameters['order_id'], '_request_type' => $parameters['request_type'], '_status' => 'pending', '_request_date' => current_time('mysql') ) )); // 添加请求物品信息 if (!empty($parameters['items']) && is_array($parameters['items'])) { update_post_meta($request_id, '_request_items', $parameters['items']); } // 发送通知邮件 $this->send_notification_email($order, $request_id, $parameters); // 记录操作日志 $order->add_order_note(sprintf('客户提交了%s请求,原因:%s', $this->get_request_type_name($parameters['request_type']), $parameters['reason'] )); return array( 'success' => true, 'request_id' => $request_id, 'message' => '售后请求已提交,我们将在24小时内处理' ); } /** * 发送通知邮件 */ private function send_notification_email($order, $request_id, $parameters) { $to = get_option('admin_email'); $subject = sprintf('新的售后请求 #%s - 订单 #%s', $request_id, $order->get_id()); $message = sprintf( "收到新的售后请求:nn请求ID:%sn订单号:%sn请求类型:%sn原因:%sn客户:%snn请登录后台查看详情。", $request_id, $order->get_id(), $this->get_request_type_name($parameters['request_type']), $parameters['reason'], $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ); wp_mail($to, $subject, $message); } }
- 为了让用户能够方便地提交售后请求,我们需要创建一个前端界面: /** * 前端售后请求表单 */ function after_sales_request_form($order_id = null) { // 如果未提供订单ID,尝试从URL参数获取 if (!$order_id && isset($_GET['order_id'])) { $order_id = intval($_GET['order_id']); } // 验证订单 if (!$order_id) { return '<div class="alert alert-danger">请提供有效的订单号</div>'; } $order = wc_get_order($order_id); if (!$order) { return '<div class="alert alert-danger">订单不存在</div>'; } // 检查当前用户是否有权访问此订单 $user_id = get_current_user_id(); if ($order->get_customer_id() !== $user_id && !current_user_can('manage_woocommerce')) { return '<div class="alert alert-danger">您无权访问此订单</div>'; } // 检查订单是否在可售后时间内(例如30天内) $order_date = $order->get_date_created(); $current_date = new DateTime(); $order_age = $current_date->diff($order_date)->days; if ($order_age > 30) { return '<div class="alert alert-warning">此订单已超过30天售后期限</div>'; } ob_start(); ?> <div class="after-sales-request-form"> <h3>提交售后请求 - 订单 #<?php echo $order->get_id(); ?></h3> <form id="after-sales-request-form" method="post"> <input type="hidden" name="order_id" value="<?php echo $order->get_id(); ?>"> <div class="form-group"> <label for="request_type">请求类型 *</label> <select name="request_type" id="request_type" required class="form-control"> <option value="">请选择请求类型</option> <option value="refund">仅退款</option> <option value="return">退货退款</option> <option value="exchange">换货</option> <option value="repair">维修</option> </select> </div> <div class="form-group"> <label for="reason">问题描述 *</label> <textarea name="reason" id="reason" rows="5" required class="form-control" placeholder="请详细描述您遇到的问题..."></textarea> </div> <div class="form-group"> <label>选择需要售后的商品</label> <div class="order-items-list"> <?php foreach ($order->get_items() as $item_id => $item): ?> <div class="order-item-checkbox"> <label> <input type="checkbox" name="items[]" value="<?php echo $item_id; ?>"> <?php echo $item->get_name(); ?> × <?php echo $item->get_quantity(); ?> </label> </div> <?php endforeach; ?> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-primary">提交请求</button> </div> <div id="form-message" class="alert" style="display:none;"></div> </form> </div> <script> jQuery(document).ready(function($) { $('#after-sales-request-form').on('submit', function(e) { e.preventDefault(); var formData = $(this).serialize(); // 显示加载状态 $('#form-message').removeClass('alert-success alert-danger') .addClass('alert-info') .html('正在提交,请稍候...') .show(); $.ajax({ url: '<?php echo rest_url('after-sales/v1/request'); ?>', method: 'POST', data: formData, beforeSend: function(xhr) { xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>'); }, success: function(response) { if (response.success) { $('#form-message').removeClass('alert-info alert-danger') .addClass('alert-success') .html('请求提交成功!' + response.message) .show(); $('#after-sales-request-form')[0].reset(); } else { $('#form-message').removeClass('alert-info alert-success') .addClass('alert-danger') .html('提交失败:' + response.message) .show(); } }, error: function(xhr, status, error) { var message = '请求失败,请稍后重试'; if (xhr.responseJSON && xhr.responseJSON.message) { message = xhr.responseJSON.message; } $('#form-message').removeClass('alert-info alert-success') .addClass('alert-danger') .html(message) .show(); } }); }); }); </script> <?php return ob_get_clean(); } // 注册短代码 add_shortcode('after_sales_form', 'after_sales_request_form');
-
- 对于需要更复杂功能的企业,我们需要构建一个模块化的售后系统: /** * 模块化售后系统主类 */ class Modular_After_Sales_System { private $modules = array(); private $settings = array(); /** * 初始化系统 */ public function init() { // 加载配置 $this->load_settings(); // 注册核心模块 $this->register_core_modules(); // 允许第三方模块注册 do_action('after_sales_system_init', $this); // 初始化所有模块 $this->initialize_modules(); } /** * 加载设置 */ private function load_settings() { $defaults = array( 'return_period' => 30, 'refund_methods' => array('original', 'credit'), 'auto_approve_threshold' => 100, // 自动审批金额阈值 'enable_partial_refunds' => true, 'restock_on_return' => true ); $this->settings = wp_parse_args( get_option('after_sales_settings', array()), $defaults ); } /** * 注册核心模块 */ private function register_core_modules() { // 请求管理模块 $this->register_module('request_manager', new Request_Manager_Module()); // 退款处理模块 $this->register_module('refund_processor', new Refund_Processor_Module()); // 物流跟踪模块 $this->register_module('logistics_tracker', new Logistics_Tracker_Module()); // 通知系统模块 $this->register_module('notification_system', new Notification_System_Module()); // 数据分析模块 $this->register_module('analytics', new Analytics_Module()); } /** * 注册模块 */ public function register_module($module_id, $module_instance) { if (isset($this->modules[$module_id])) { error_log(sprintf('售后系统模块 %s 已注册', $module_id)); return false; } $this->modules[$module_id] = $module_instance; return true; } /** * 获取模块 */ public function get_module($module_id) { return isset($this->modules[$module_id]) ? $this->modules[$module_id] : null; } } /** * 请求管理模块 */ class Request_Manager_Module { private $status_flow = array( 'pending' => array('processing', 'cancelled'), 'processing' => array('approved', 'rejected', 'cancelled'), 'approved' => array('refunded', 'exchanged', 'completed'), 'rejected' => array(), 'cancelled' => array(), 'refunded' => array('completed'), 'exchanged' => array('completed'), 'completed' => array() ); /** * 处理状态转换 */ public function change_request_status($request_id, $new_status, $notes = '') { $current_status = get_post_meta($request_id, '_status', true); // 检查状态转换是否有效 if (!isset($this->status_flow[$current_status]) || !in_array($new_status, $this->status_flow[$current_status])) { return new WP_Error('invalid_status_transition', sprintf('无法从状态 %s 转换到 %s', $current_status, $new_status)); } // 更新状态 update_post_meta($request_id, '_status', $new_status); // 记录状态变更历史 $this->add_status_history($request_id, $current_status, $new_status, $notes); // 触发状态变更钩子 do_action('after_sales_request_status_changed', $request_id, $current_status, $new_status, $notes); return true; } /** * 添加状态历史记录 */ private function add_status_history($request_id, $from_status, $to_status, $notes) { $history = get_post_meta($request_id, '_status_history', true); if (!is_array($history)) { $history = array(); } $history[] = array( 'from' => $from_status, 'to' => $to_status, 'timestamp' => current_time('mysql'), 'user_id' => get_current_user_id(), 'notes' => $notes ); update_post_meta($request_id, '_status_history', $history); } } /** * 退款处理模块 */ class Refund_Processor_Module { /** * 处理退款请求 */ public function process_refund($request_id) { $request = get_post($request_id); if (!$request || $request->post_type !== 'after_sales_request') { return new WP_Error('invalid_request', '无效的售后请求'); } $order_id = get_post_meta($request_id, '_order_id', true); $order = wc_get_order($order_id); if (!$order) { return new WP_Error('invalid_order', '订单不存在'); } // 获取退款金额 $refund_amount = $this->calculate_refund_amount($request_id, $order); // 创建退款 $refund = wc_create_refund(array( 'amount' => $refund_amount, 'reason' => get_post_meta($request_id, '_reason', true), 'order_id' => $order_id, 'refund_payment' => true, 'restock_items' => $this->should_restock_items($request_id) )); if (is_wp_error($refund)) { return $refund; } // 更新请求状态 update_post_meta($request_id, '_refund_id', $refund->get_id()); update_post_meta($request_id, '_refund_amount', $refund_amount); update_post_meta($request_id, '_refund_date', current_time('mysql')); return $refund->get_id(); } /** * 计算退款金额 */ private function calculate_refund_amount($request_id, $order) { $request_type = get_post_meta($request_id, '_request_type', true); $request_items = get_post_meta($request_id, '_request_items', true); // 如果是全额退款 if (empty($request_items)) { return $order->get_total(); } // 计算部分退款金额 $refund_amount = 0; foreach ($request_items as $item_id) { $item = $order->get_item($item_id); if ($item) { $refund_amount += $item->get_total(); } } // 考虑运费和税费 $refund_amount = $this->adjust_for_fees_and_taxes($refund_amount, $order, $request_items); return $refund_amount; } } /** * 物流跟踪模块 */ class Logistics_Tracker_Module { /** * 添加快递跟踪信息 */ public function add_tracking_info($request_id, $carrier, $tracking_number, $shipping_date = null) { $tracking_info = array( 'carrier' => $carrier, 'tracking_number' => $tracking_number, 'shipping_date' => $shipping_date ?: current_time('mysql'), 'status' => 'shipped', 'last_updated' => current_time('mysql') ); update_post_meta($request_id, '_tracking_info', $tracking_info); // 调用快递API获取实时跟踪信息 $this->update_tracking_status($request_id, $carrier, $tracking_number); return true; } /** * 更新跟踪状态 */ private function update_tracking_status($request_id, $carrier, $tracking_number) { // 这里可以集成第三方快递API,如快递鸟、快递100等 $api_response = $this->call_carrier_api($carrier, $tracking_number); if ($api_response && isset($api_response['status'])) { update_post_meta($request_id, '_tracking_status', $api_response['status']); update_post_meta($request_id, '_tracking_details', $api_response['details']); } } }
- /** * RESTful API接口类 */ class After_Sales_REST_API { /** * 注册所有API端点 */ public function register_endpoints() { // 批量获取售后请求 register_rest_route('after-sales/v2', '/requests', array( array( 'methods' => 'GET', 'callback' => array($this, 'get_requests'), 'permission_callback' => array($this, 'check_admin_permission'), 'args' => $this->get_collection_params() ), array( 'methods' => 'POST', 'callback' => array($this, 'create_request'), 'permission_callback' => array($this, 'check_create_permission') ) )); // 单个售后请求操作 register_rest_route('after-sales/v2', '/requests/(?P<id>d+)', array( array( 'methods' => 'GET', 'callback' => array($this, 'get_request'), 'permission_callback' => array($this, 'check_request_permission') ), array( 'methods' => 'PUT', 'callback' => array($this, 'update_request'), 'permission_callback' => array($this, 'check_admin_permission') ), array( 'methods' => 'DELETE', 'callback' => array($this, 'delete_request'), 'permission_callback' => array($this, 'check_admin_permission') ) )); // 状态操作 register_rest_route('after-sales/v2', '/requests/(?P<id>d+)/status', array( 'methods' => 'PUT', 'callback' => array($this, 'update_status'), 'permission_callback' => array($this, 'check_admin_permission') )); // 退款操作 register_rest_route('after-sales/v2', '/requests/(?P<id>d+)/refund', array( 'methods' => 'POST', 'callback' => array($this, 'process_refund'), 'permission_callback' => array($this, 'check_admin_permission') )); // 统计信息 register_rest_route('after-sales/v2', '/stats', array( 'methods' => 'GET', 'callback' => array($this, 'get_statistics'), 'permission_callback' => array($this, 'check_admin_permission') )); } /** * 获取售后请求列表 */ public function get_requests($request) { $params = $request->get_params(); $args = array( 'post_type' => 'after_sales_request', 'post_status' => 'publish', 'posts_per_page' => isset($params['per_page']) ? intval($params['per_page']) : 10, 'paged' => isset($params['page']) ? intval($params['page']) : 1 ); // 添加过滤条件 if (!empty($params['status'])) { $args['meta_query'][] = array( 'key' => '_status', 'value' => $params['status'] ); } if (!empty($params['order_id'])) { $args['meta_query'][] = array( 'key' => '_order_id', 'value' => intval($params['order_id']) ); } if (!empty($params['customer_id'])) { $args['author'] = intval($params['customer_id']); } if (!empty($params['date_from'])) { $args['date_query'][] = array( 'after' => $params['date_from'], 'inclusive' => true ); } if (!empty($params['date_to'])) { $args['date_query'][] = array( 'before' => $params['date_to'], 'inclusive' => true ); } $query = new WP_Query($args); $requests = array(); foreach ($query->posts as $post) { $requests[] = $this->prepare_request_data($post); } $response = new WP_REST_Response($requests); // 添加分页头信息 $total = $query->found_posts; $total_pages = ceil($total / $args['posts_per_page']); $response->header('X-WP-Total', $total); $response->header('X-WP-TotalPages', $total_pages); return $response; } /** * 准备请求数据 */ private function prepare_request_data($post) { $meta = get_post_meta($post->ID); $data = array( 'id' => $post->ID, 'title' => $post->post_title, 'description' => $post->post_content, 'date' => $post->post_date, 'customer_id' => $post->post_author, 'order_id' => isset($meta['_order_id'][0]) ? $meta['_order_id'][0] : null, 'request_type' => isset($meta['_request_type'][0]) ? $meta['_request_type'][0] : null, 'status' => isset($meta['_status'][0]) ? $meta['_status'][0] : 'pending', 'items' => isset($meta['_request_items'][0]) ? maybe_unserialize($meta['_request_items'][0]) : array(), 'refund_amount' => isset($meta['_refund_amount'][0]) ? $meta['_refund_amount'][0] : null, 'tracking_info' => isset($meta['_tracking_info'][0]) ? maybe_unserialize($meta['_tracking_info'][0]) : null, 'status_history' => isset($meta['_status_history'][0]) ? maybe_unserialize($meta['_status_history'][0]) : array() ); // 获取客户信息 $customer = get_userdata($post->post_author); if ($customer) { $data['customer'] = array( 'name' => $customer->display_name, 'email' => $customer->user_email ); } // 获取订单信息 if ($data['order_id']) { $order = wc_get_order($data['order_id']); if ($order) { $data['order'] = array( 'total' => $order->get_total(), 'currency' => $order->get_currency(), 'date' => $order->get_date_created()->date('Y-m-d H:i:s') ); } } return $data; } /** * 处理退款 */ public function process_refund($request) { $request_id = $request['id']; $parameters = $request->get_params(); $refund_processor = $GLOBALS['after_sales_system']->get_module('refund_processor'); if (!$refund_processor) { return new WP_Error('module_not_found', '退款处理模块未找到', array('status' => 500)); } $result = $refund_processor->process_refund($request_id); if (is_wp_error($result)) { return $result; } return array( 'success' => true, 'refund_id' => $result, 'message' => '退款处理成功' ); } /** * 获取统计信息 */ public function get_statistics($request) { $params = $request->get_params(); $period = isset($params['period']) ? $params['period'] : 'month'; // day, week, month, year $stats = array( 'total_requests' => $this->get_total_requests($period), 'by_status' => $this->get_requests_by_status($period), 'by_type' => $this->get_requests_by_type($period), 'refund_amount' => $this->get_refund_amount($period), 'processing_time' => $this->get_average_processing_time($period) ); return $stats; } /** * 获取请求总数 */ private function get_total_requests($period) { $date_query = $this->get_date_query_for_period($period); $args = array( 'post_type' => 'after_sales_request', 'post_status' => 'publish', 'date_query' => $date_query, 'fields' => 'ids', 'posts_per_page' => -1 ); $query = new WP_Query($args); return $query->found_posts; } }
- /** * 后台管理界面 */ class After_Sales_Admin_Interface { /** * 初始化管理界面 */ public function init() { add_action('admin_menu', array($this, 'add_admin_pages')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); add_action('add_meta_boxes', array($this, 'add_meta_boxes')); add_action('save_post_after_sales_request', array($this, 'save_request_meta')); // 添加列表页列 add_filter('manage_after_sales_request_posts_columns', array($this, 'add_custom_columns')); add_action('manage_after_sales_request_posts_custom_column', array($this, 'render_custom_columns'), 10, 2); add_filter('manage_edit-after_sales_request_sortable_columns', array($this, 'make_columns_sortable')); } /** * 添加管理页面 */ public function add_admin_pages() { // 主菜单 add_menu_page( '售后管理', '售后管理', 'manage_woocommerce', 'after-sales', array($this, 'render_dashboard'), 'dashicons-undo', 56 ); // 子菜单 add_submenu_page( 'after-sales', '售后仪表盘', '仪表盘', 'manage_woocommerce', 'after-sales', array($this, 'render_dashboard') ); add_submenu_page( 'after-sales', '售后请求', '所有请求', 'manage_woocommerce', 'edit.php?post_type=after_sales_request' ); add_submenu_page( 'after-sales', '售后设置', '设置', 'manage_woocommerce', 'after-sales-settings', array($this, 'render_settings_page') ); add_submenu_page( 'after-sales', '售后报表', '报表', 'manage_woocommerce', 'after-sales-reports', array($this, 'render_reports_page') ); } /** * 渲染仪表盘 */ public function render_dashboard() { ?> <div class="wrap after-sales-dashboard"> <h1 class="wp-heading-inline">售后管理仪表盘</h1> <div class="dashboard-widgets"> <div class="widget-row"> <div class="widget-card"> <h3>待处理请求</h3> <div class="widget-value" id="pending-requests">0</div> <a href="<?php echo admin_url('edit.php?post_type=after_sales_request&status=pending'); ?>" class="widget-link">查看详情</a> </div> <div class="widget-card"> <h3>今日请求</h3> <div class="widget-value" id="today-requests">0</div> </div> <div class="widget-card"> <h3>本月退款金额</h3> <div class="widget-value" id="monthly-refund">¥0</div> </div> <div class="widget-card"> <h3>平均处理时间</h3> <div class="widget-value" id="avg-processing-time">0小时</div> </div> </div> <div class="widget-row"> <div class="widget-large-card"> <h3>请求趋势</h3> <div class="chart-container"> <canvas id="requests-trend-chart"></canvas> </div> </div> <div class="widget-large-card"> <h3>状态分布</h3>
在当今竞争激烈的电商环境中,完善的售后与退货处理系统不仅是提升客户满意度的关键,更是企业运营效率的重要保障。根据Statista的数据显示,超过60%的在线购物者在购买前会查看退货政策,而高效的退货处理可以将客户保留率提高30%以上。
对于使用WordPress构建电商网站的开发者和企业来说,构建一个稳定、高效的售后与退货处理接口尤为重要。WordPress作为全球最流行的内容管理系统,配合WooCommerce等电商插件,为开发者提供了强大的基础框架。然而,标准解决方案往往无法满足企业的个性化需求,这就需要我们深入代码层面,构建定制化的售后接口。
本文将基于WordPress开源系统,为行业新人和程序员提供三种周全的售后与退货处理接口构建方法,从基础到高级,逐步深入。
WooCommerce作为WordPress最流行的电商插件,已经内置了基本的订单管理和退款功能。要构建售后接口,首先需要理解其核心数据结构:
// WooCommerce订单退款相关的主要数据表
// wp_woocommerce_order_items - 订单项目
// wp_woocommerce_order_itemmeta - 订单项目元数据
// wp_posts (post_type = 'shop_order') - 订单主数据
// wp_postmeta - 订单元数据
// wp_comments (comment_type = 'order_note') - 订单备注/历史
// 退款创建的基本流程
function create_woocommerce_refund($order_id, $amount, $reason) {
$order = wc_get_order($order_id);
// 创建退款对象
$refund = wc_create_refund(array(
'amount' => $amount,
'reason' => $reason,
'order_id' => $order_id,
'refund_payment' => true, // 是否退款到支付方式
'restock_items' => true // 是否恢复库存
));
return $refund;
}
基于WooCommerce原生功能,我们可以构建一个基础的售后请求处理接口:
/**
* 售后请求处理类
*/
class Basic_After_Sales_Handler {
private $request_types = array('refund', 'return', 'exchange', 'repair');
/**
* 初始化售后系统
*/
public function init() {
// 注册售后请求自定义帖子类型
add_action('init', array($this, 'register_after_sales_post_type'));
// 添加REST API端点
add_action('rest_api_init', array($this, 'register_rest_endpoints'));
// 添加管理界面
add_action('admin_menu', array($this, 'add_admin_menu'));
}
/**
* 注册售后请求自定义帖子类型
*/
public function register_after_sales_post_type() {
$labels = array(
'name' => '售后请求',
'singular_name' => '售后请求',
'menu_name' => '售后管理',
'add_new' => '添加请求',
'add_new_item' => '添加新售后请求',
'edit_item' => '编辑售后请求',
'new_item' => '新售后请求',
'view_item' => '查看售后请求',
'search_items' => '搜索售后请求',
'not_found' => '未找到售后请求',
'not_found_in_trash' => '回收站中无售后请求'
);
$args = array(
'labels' => $labels,
'public' => false,
'publicly_queryable' => false,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'after-sales'),
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'menu_position' => 56,
'menu_icon' => 'dashicons-undo',
'supports' => array('title', 'editor', 'author', 'custom-fields'),
'show_in_rest' => true // 启用REST API支持
);
register_post_type('after_sales_request', $args);
// 注册售后状态分类
register_taxonomy(
'after_sales_status',
'after_sales_request',
array(
'label' => '售后状态',
'rewrite' => array('slug' => 'after-sales-status'),
'hierarchical' => true,
'show_in_rest' => true
)
);
}
/**
* 注册REST API端点
*/
public function register_rest_endpoints() {
// 创建售后请求端点
register_rest_route('after-sales/v1', '/request', array(
'methods' => 'POST',
'callback' => array($this, 'handle_after_sales_request'),
'permission_callback' => array($this, 'check_request_permission'),
'args' => array(
'order_id' => array(
'required' => true,
'validate_callback' => function($param) {
return is_numeric($param) && $param > 0;
}
),
'request_type' => array(
'required' => true,
'validate_callback' => function($param) {
$allowed_types = array('refund', 'return', 'exchange', 'repair');
return in_array($param, $allowed_types);
}
),
'reason' => array(
'required' => true,
'sanitize_callback' => 'sanitize_text_field'
),
'items' => array(
'required' => false,
'validate_callback' => function($param) {
return is_array($param);
}
)
)
));
// 获取售后请求状态端点
register_rest_route('after-sales/v1', '/status/(?P<id>d+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_request_status'),
'permission_callback' => function() {
return current_user_can('read');
}
));
}
/**
* 处理售后请求
*/
public function handle_after_sales_request($request) {
$parameters = $request->get_params();
// 验证订单是否存在且属于当前用户
$order = wc_get_order($parameters['order_id']);
if (!$order) {
return new WP_Error('invalid_order', '订单不存在', array('status' => 404));
}
// 检查用户权限
$user_id = get_current_user_id();
if ($order->get_customer_id() !== $user_id && !current_user_can('manage_woocommerce')) {
return new WP_Error('permission_denied', '无权访问此订单', array('status' => 403));
}
// 创建售后请求记录
$request_id = wp_insert_post(array(
'post_title' => sprintf('售后请求 - 订单 #%s', $parameters['order_id']),
'post_content' => $parameters['reason'],
'post_status' => 'publish',
'post_type' => 'after_sales_request',
'post_author' => $user_id,
'meta_input' => array(
'_order_id' => $parameters['order_id'],
'_request_type' => $parameters['request_type'],
'_status' => 'pending',
'_request_date' => current_time('mysql')
)
));
// 添加请求物品信息
if (!empty($parameters['items']) && is_array($parameters['items'])) {
update_post_meta($request_id, '_request_items', $parameters['items']);
}
// 发送通知邮件
$this->send_notification_email($order, $request_id, $parameters);
// 记录操作日志
$order->add_order_note(sprintf('客户提交了%s请求,原因:%s',
$this->get_request_type_name($parameters['request_type']),
$parameters['reason']
));
return array(
'success' => true,
'request_id' => $request_id,
'message' => '售后请求已提交,我们将在24小时内处理'
);
}
/**
* 发送通知邮件
*/
private function send_notification_email($order, $request_id, $parameters) {
$to = get_option('admin_email');
$subject = sprintf('新的售后请求 #%s - 订单 #%s', $request_id, $order->get_id());
$message = sprintf(
"收到新的售后请求:nn请求ID:%sn订单号:%sn请求类型:%sn原因:%sn客户:%snn请登录后台查看详情。",
$request_id,
$order->get_id(),
$this->get_request_type_name($parameters['request_type']),
$parameters['reason'],
$order->get_billing_first_name() . ' ' . $order->get_billing_last_name()
);
wp_mail($to, $subject, $message);
}
}
为了让用户能够方便地提交售后请求,我们需要创建一个前端界面:
/**
* 前端售后请求表单
*/
function after_sales_request_form($order_id = null) {
// 如果未提供订单ID,尝试从URL参数获取
if (!$order_id && isset($_GET['order_id'])) {
$order_id = intval($_GET['order_id']);
}
// 验证订单
if (!$order_id) {
return '<div class="alert alert-danger">请提供有效的订单号</div>';
}
$order = wc_get_order($order_id);
if (!$order) {
return '<div class="alert alert-danger">订单不存在</div>';
}
// 检查当前用户是否有权访问此订单
$user_id = get_current_user_id();
if ($order->get_customer_id() !== $user_id && !current_user_can('manage_woocommerce')) {
return '<div class="alert alert-danger">您无权访问此订单</div>';
}
// 检查订单是否在可售后时间内(例如30天内)
$order_date = $order->get_date_created();
$current_date = new DateTime();
$order_age = $current_date->diff($order_date)->days;
if ($order_age > 30) {
return '<div class="alert alert-warning">此订单已超过30天售后期限</div>';
}
ob_start();
?>
<div class="after-sales-request-form">
<h3>提交售后请求 - 订单 #<?php echo $order->get_id(); ?></h3>
<form id="after-sales-request-form" method="post">
<input type="hidden" name="order_id" value="<?php echo $order->get_id(); ?>">
<div class="form-group">
<label for="request_type">请求类型 *</label>
<select name="request_type" id="request_type" required class="form-control">
<option value="">请选择请求类型</option>
<option value="refund">仅退款</option>
<option value="return">退货退款</option>
<option value="exchange">换货</option>
<option value="repair">维修</option>
</select>
</div>
<div class="form-group">
<label for="reason">问题描述 *</label>
<textarea name="reason" id="reason" rows="5" required class="form-control"
placeholder="请详细描述您遇到的问题..."></textarea>
</div>
<div class="form-group">
<label>选择需要售后的商品</label>
<div class="order-items-list">
<?php foreach ($order->get_items() as $item_id => $item): ?>
<div class="order-item-checkbox">
<label>
<input type="checkbox" name="items[]" value="<?php echo $item_id; ?>">
<?php echo $item->get_name(); ?> × <?php echo $item->get_quantity(); ?>
</label>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交请求</button>
</div>
<div id="form-message" class="alert" style="display:none;"></div>
</form>
</div>
<script>
jQuery(document).ready(function($) {
$('#after-sales-request-form').on('submit', function(e) {
e.preventDefault();
var formData = $(this).serialize();
// 显示加载状态
$('#form-message').removeClass('alert-success alert-danger')
.addClass('alert-info')
.html('正在提交,请稍候...')
.show();
$.ajax({
url: '<?php echo rest_url('after-sales/v1/request'); ?>',
method: 'POST',
data: formData,
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', '<?php echo wp_create_nonce('wp_rest'); ?>');
},
success: function(response) {
if (response.success) {
$('#form-message').removeClass('alert-info alert-danger')
.addClass('alert-success')
.html('请求提交成功!' + response.message)
.show();
$('#after-sales-request-form')[0].reset();
} else {
$('#form-message').removeClass('alert-info alert-success')
.addClass('alert-danger')
.html('提交失败:' + response.message)
.show();
}
},
error: function(xhr, status, error) {
var message = '请求失败,请稍后重试';
if (xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
$('#form-message').removeClass('alert-info alert-success')
.addClass('alert-danger')
.html(message)
.show();
}
});
});
});
</script>
<?php
return ob_get_clean();
}
// 注册短代码
add_shortcode('after_sales_form', 'after_sales_request_form');
对于需要更复杂功能的企业,我们需要构建一个模块化的售后系统:
/**
* 模块化售后系统主类
*/
class Modular_After_Sales_System {
private $modules = array();
private $settings = array();
/**
* 初始化系统
*/
public function init() {
// 加载配置
$this->load_settings();
// 注册核心模块
$this->register_core_modules();
// 允许第三方模块注册
do_action('after_sales_system_init', $this);
// 初始化所有模块
$this->initialize_modules();
}
/**
* 加载设置
*/
private function load_settings() {
$defaults = array(
'return_period' => 30,
'refund_methods' => array('original', 'credit'),
'auto_approve_threshold' => 100, // 自动审批金额阈值
'enable_partial_refunds' => true,
'restock_on_return' => true
);
$this->settings = wp_parse_args(
get_option('after_sales_settings', array()),
$defaults
);
}
/**
* 注册核心模块
*/
private function register_core_modules() {
// 请求管理模块
$this->register_module('request_manager', new Request_Manager_Module());
// 退款处理模块
$this->register_module('refund_processor', new Refund_Processor_Module());
// 物流跟踪模块
$this->register_module('logistics_tracker', new Logistics_Tracker_Module());
// 通知系统模块
$this->register_module('notification_system', new Notification_System_Module());
// 数据分析模块
$this->register_module('analytics', new Analytics_Module());
}
/**
* 注册模块
*/
public function register_module($module_id, $module_instance) {
if (isset($this->modules[$module_id])) {
error_log(sprintf('售后系统模块 %s 已注册', $module_id));
return false;
}
$this->modules[$module_id] = $module_instance;
return true;
}
/**
* 获取模块
*/
public function get_module($module_id) {
return isset($this->modules[$module_id]) ? $this->modules[$module_id] : null;
}
}
/**
* 请求管理模块
*/
class Request_Manager_Module {
private $status_flow = array(
'pending' => array('processing', 'cancelled'),
'processing' => array('approved', 'rejected', 'cancelled'),
'approved' => array('refunded', 'exchanged', 'completed'),
'rejected' => array(),
'cancelled' => array(),
'refunded' => array('completed'),
'exchanged' => array('completed'),
'completed' => array()
);
/**
* 处理状态转换
*/
public function change_request_status($request_id, $new_status, $notes = '') {
$current_status = get_post_meta($request_id, '_status', true);
// 检查状态转换是否有效
if (!isset($this->status_flow[$current_status]) ||
!in_array($new_status, $this->status_flow[$current_status])) {
return new WP_Error('invalid_status_transition',
sprintf('无法从状态 %s 转换到 %s', $current_status, $new_status));
}
// 更新状态
update_post_meta($request_id, '_status', $new_status);
// 记录状态变更历史
$this->add_status_history($request_id, $current_status, $new_status, $notes);
// 触发状态变更钩子
do_action('after_sales_request_status_changed', $request_id, $current_status, $new_status, $notes);
return true;
}
/**
* 添加状态历史记录
*/
private function add_status_history($request_id, $from_status, $to_status, $notes) {
$history = get_post_meta($request_id, '_status_history', true);
if (!is_array($history)) {
$history = array();
}
$history[] = array(
'from' => $from_status,
'to' => $to_status,
'timestamp' => current_time('mysql'),
'user_id' => get_current_user_id(),
'notes' => $notes
);
update_post_meta($request_id, '_status_history', $history);
}
}
/**
* 退款处理模块
*/
class Refund_Processor_Module {
/**
* 处理退款请求
*/
public function process_refund($request_id) {
$request = get_post($request_id);
if (!$request || $request->post_type !== 'after_sales_request') {
return new WP_Error('invalid_request', '无效的售后请求');
}
$order_id = get_post_meta($request_id, '_order_id', true);
$order = wc_get_order($order_id);
if (!$order) {
return new WP_Error('invalid_order', '订单不存在');
}
// 获取退款金额
$refund_amount = $this->calculate_refund_amount($request_id, $order);
// 创建退款
$refund = wc_create_refund(array(
'amount' => $refund_amount,
'reason' => get_post_meta($request_id, '_reason', true),
'order_id' => $order_id,
'refund_payment' => true,
'restock_items' => $this->should_restock_items($request_id)
));
if (is_wp_error($refund)) {
return $refund;
}
// 更新请求状态
update_post_meta($request_id, '_refund_id', $refund->get_id());
update_post_meta($request_id, '_refund_amount', $refund_amount);
update_post_meta($request_id, '_refund_date', current_time('mysql'));
return $refund->get_id();
}
/**
* 计算退款金额
*/
private function calculate_refund_amount($request_id, $order) {
$request_type = get_post_meta($request_id, '_request_type', true);
$request_items = get_post_meta($request_id, '_request_items', true);
// 如果是全额退款
if (empty($request_items)) {
return $order->get_total();
}
// 计算部分退款金额
$refund_amount = 0;
foreach ($request_items as $item_id) {
$item = $order->get_item($item_id);
if ($item) {
$refund_amount += $item->get_total();
}
}
// 考虑运费和税费
$refund_amount = $this->adjust_for_fees_and_taxes($refund_amount, $order, $request_items);
return $refund_amount;
}
}
/**
* 物流跟踪模块
*/
class Logistics_Tracker_Module {
/**
* 添加快递跟踪信息
*/
public function add_tracking_info($request_id, $carrier, $tracking_number, $shipping_date = null) {
$tracking_info = array(
'carrier' => $carrier,
'tracking_number' => $tracking_number,
'shipping_date' => $shipping_date ?: current_time('mysql'),
'status' => 'shipped',
'last_updated' => current_time('mysql')
);
update_post_meta($request_id, '_tracking_info', $tracking_info);
// 调用快递API获取实时跟踪信息
$this->update_tracking_status($request_id, $carrier, $tracking_number);
return true;
}
/**
* 更新跟踪状态
*/
private function update_tracking_status($request_id, $carrier, $tracking_number) {
// 这里可以集成第三方快递API,如快递鸟、快递100等
$api_response = $this->call_carrier_api($carrier, $tracking_number);
if ($api_response && isset($api_response['status'])) {
update_post_meta($request_id, '_tracking_status', $api_response['status']);
update_post_meta($request_id, '_tracking_details', $api_response['details']);
}
}
}
/**
* RESTful API接口类
*/
class After_Sales_REST_API {
/**
* 注册所有API端点
*/
public function register_endpoints() {
// 批量获取售后请求
register_rest_route('after-sales/v2', '/requests', array(
array(
'methods' => 'GET',
'callback' => array($this, 'get_requests'),
'permission_callback' => array($this, 'check_admin_permission'),
'args' => $this->get_collection_params()
),
array(
'methods' => 'POST',
'callback' => array($this, 'create_request'),
'permission_callback' => array($this, 'check_create_permission')
)
));
// 单个售后请求操作
register_rest_route('after-sales/v2', '/requests/(?P<id>d+)', array(
array(
'methods' => 'GET',
'callback' => array($this, 'get_request'),
'permission_callback' => array($this, 'check_request_permission')
),
array(
'methods' => 'PUT',
'callback' => array($this, 'update_request'),
'permission_callback' => array($this, 'check_admin_permission')
),
array(
'methods' => 'DELETE',
'callback' => array($this, 'delete_request'),
'permission_callback' => array($this, 'check_admin_permission')
)
));
// 状态操作
register_rest_route('after-sales/v2', '/requests/(?P<id>d+)/status', array(
'methods' => 'PUT',
'callback' => array($this, 'update_status'),
'permission_callback' => array($this, 'check_admin_permission')
));
// 退款操作
register_rest_route('after-sales/v2', '/requests/(?P<id>d+)/refund', array(
'methods' => 'POST',
'callback' => array($this, 'process_refund'),
'permission_callback' => array($this, 'check_admin_permission')
));
// 统计信息
register_rest_route('after-sales/v2', '/stats', array(
'methods' => 'GET',
'callback' => array($this, 'get_statistics'),
'permission_callback' => array($this, 'check_admin_permission')
));
}
/**
* 获取售后请求列表
*/
public function get_requests($request) {
$params = $request->get_params();
$args = array(
'post_type' => 'after_sales_request',
'post_status' => 'publish',
'posts_per_page' => isset($params['per_page']) ? intval($params['per_page']) : 10,
'paged' => isset($params['page']) ? intval($params['page']) : 1
);
// 添加过滤条件
if (!empty($params['status'])) {
$args['meta_query'][] = array(
'key' => '_status',
'value' => $params['status']
);
}
if (!empty($params['order_id'])) {
$args['meta_query'][] = array(
'key' => '_order_id',
'value' => intval($params['order_id'])
);
}
if (!empty($params['customer_id'])) {
$args['author'] = intval($params['customer_id']);
}
if (!empty($params['date_from'])) {
$args['date_query'][] = array(
'after' => $params['date_from'],
'inclusive' => true
);
}
if (!empty($params['date_to'])) {
$args['date_query'][] = array(
'before' => $params['date_to'],
'inclusive' => true
);
}
$query = new WP_Query($args);
$requests = array();
foreach ($query->posts as $post) {
$requests[] = $this->prepare_request_data($post);
}
$response = new WP_REST_Response($requests);
// 添加分页头信息
$total = $query->found_posts;
$total_pages = ceil($total / $args['posts_per_page']);
$response->header('X-WP-Total', $total);
$response->header('X-WP-TotalPages', $total_pages);
return $response;
}
/**
* 准备请求数据
*/
private function prepare_request_data($post) {
$meta = get_post_meta($post->ID);
$data = array(
'id' => $post->ID,
'title' => $post->post_title,
'description' => $post->post_content,
'date' => $post->post_date,
'customer_id' => $post->post_author,
'order_id' => isset($meta['_order_id'][0]) ? $meta['_order_id'][0] : null,
'request_type' => isset($meta['_request_type'][0]) ? $meta['_request_type'][0] : null,
'status' => isset($meta['_status'][0]) ? $meta['_status'][0] : 'pending',
'items' => isset($meta['_request_items'][0]) ? maybe_unserialize($meta['_request_items'][0]) : array(),
'refund_amount' => isset($meta['_refund_amount'][0]) ? $meta['_refund_amount'][0] : null,
'tracking_info' => isset($meta['_tracking_info'][0]) ? maybe_unserialize($meta['_tracking_info'][0]) : null,
'status_history' => isset($meta['_status_history'][0]) ? maybe_unserialize($meta['_status_history'][0]) : array()
);
// 获取客户信息
$customer = get_userdata($post->post_author);
if ($customer) {
$data['customer'] = array(
'name' => $customer->display_name,
'email' => $customer->user_email
);
}
// 获取订单信息
if ($data['order_id']) {
$order = wc_get_order($data['order_id']);
if ($order) {
$data['order'] = array(
'total' => $order->get_total(),
'currency' => $order->get_currency(),
'date' => $order->get_date_created()->date('Y-m-d H:i:s')
);
}
}
return $data;
}
/**
* 处理退款
*/
public function process_refund($request) {
$request_id = $request['id'];
$parameters = $request->get_params();
$refund_processor = $GLOBALS['after_sales_system']->get_module('refund_processor');
if (!$refund_processor) {
return new WP_Error('module_not_found', '退款处理模块未找到', array('status' => 500));
}
$result = $refund_processor->process_refund($request_id);
if (is_wp_error($result)) {
return $result;
}
return array(
'success' => true,
'refund_id' => $result,
'message' => '退款处理成功'
);
}
/**
* 获取统计信息
*/
public function get_statistics($request) {
$params = $request->get_params();
$period = isset($params['period']) ? $params['period'] : 'month'; // day, week, month, year
$stats = array(
'total_requests' => $this->get_total_requests($period),
'by_status' => $this->get_requests_by_status($period),
'by_type' => $this->get_requests_by_type($period),
'refund_amount' => $this->get_refund_amount($period),
'processing_time' => $this->get_average_processing_time($period)
);
return $stats;
}
/**
* 获取请求总数
*/
private function get_total_requests($period) {
$date_query = $this->get_date_query_for_period($period);
$args = array(
'post_type' => 'after_sales_request',
'post_status' => 'publish',
'date_query' => $date_query,
'fields' => 'ids',
'posts_per_page' => -1
);
$query = new WP_Query($args);
return $query->found_posts;
}
}
/**
* RESTful API接口类
*/
class After_Sales_REST_API {
/**
* 注册所有API端点
*/
public function register_endpoints() {
// 批量获取售后请求
register_rest_route('after-sales/v2', '/requests', array(
array(
'methods' => 'GET',
'callback' => array($this, 'get_requests'),
'permission_callback' => array($this, 'check_admin_permission'),
'args' => $this->get_collection_params()
),
array(
'methods' => 'POST',
'callback' => array($this, 'create_request'),
'permission_callback' => array($this, 'check_create_permission')
)
));
// 单个售后请求操作
register_rest_route('after-sales/v2', '/requests/(?P<id>d+)', array(
array(
'methods' => 'GET',
'callback' => array($this, 'get_request'),
'permission_callback' => array($this, 'check_request_permission')
),
array(
'methods' => 'PUT',
'callback' => array($this, 'update_request'),
'permission_callback' => array($this, 'check_admin_permission')
),
array(
'methods' => 'DELETE',
'callback' => array($this, 'delete_request'),
'permission_callback' => array($this, 'check_admin_permission')
)
));
// 状态操作
register_rest_route('after-sales/v2', '/requests/(?P<id>d+)/status', array(
'methods' => 'PUT',
'callback' => array($this, 'update_status'),
'permission_callback' => array($this, 'check_admin_permission')
));
// 退款操作
register_rest_route('after-sales/v2', '/requests/(?P<id>d+)/refund', array(
'methods' => 'POST',
'callback' => array($this, 'process_refund'),
'permission_callback' => array($this, 'check_admin_permission')
));
// 统计信息
register_rest_route('after-sales/v2', '/stats', array(
'methods' => 'GET',
'callback' => array($this, 'get_statistics'),
'permission_callback' => array($this, 'check_admin_permission')
));
}
/**
* 获取售后请求列表
*/
public function get_requests($request) {
$params = $request->get_params();
$args = array(
'post_type' => 'after_sales_request',
'post_status' => 'publish',
'posts_per_page' => isset($params['per_page']) ? intval($params['per_page']) : 10,
'paged' => isset($params['page']) ? intval($params['page']) : 1
);
// 添加过滤条件
if (!empty($params['status'])) {
$args['meta_query'][] = array(
'key' => '_status',
'value' => $params['status']
);
}
if (!empty($params['order_id'])) {
$args['meta_query'][] = array(
'key' => '_order_id',
'value' => intval($params['order_id'])
);
}
if (!empty($params['customer_id'])) {
$args['author'] = intval($params['customer_id']);
}
if (!empty($params['date_from'])) {
$args['date_query'][] = array(
'after' => $params['date_from'],
'inclusive' => true
);
}
if (!empty($params['date_to'])) {
$args['date_query'][] = array(
'before' => $params['date_to'],
'inclusive' => true
);
}
$query = new WP_Query($args);
$requests = array();
foreach ($query->posts as $post) {
$requests[] = $this->prepare_request_data($post);
}
$response = new WP_REST_Response($requests);
// 添加分页头信息
$total = $query->found_posts;
$total_pages = ceil($total / $args['posts_per_page']);
$response->header('X-WP-Total', $total);
$response->header('X-WP-TotalPages', $total_pages);
return $response;
}
/**
* 准备请求数据
*/
private function prepare_request_data($post) {
$meta = get_post_meta($post->ID);
$data = array(
'id' => $post->ID,
'title' => $post->post_title,
'description' => $post->post_content,
'date' => $post->post_date,
'customer_id' => $post->post_author,
'order_id' => isset($meta['_order_id'][0]) ? $meta['_order_id'][0] : null,
'request_type' => isset($meta['_request_type'][0]) ? $meta['_request_type'][0] : null,
'status' => isset($meta['_status'][0]) ? $meta['_status'][0] : 'pending',
'items' => isset($meta['_request_items'][0]) ? maybe_unserialize($meta['_request_items'][0]) : array(),
'refund_amount' => isset($meta['_refund_amount'][0]) ? $meta['_refund_amount'][0] : null,
'tracking_info' => isset($meta['_tracking_info'][0]) ? maybe_unserialize($meta['_tracking_info'][0]) : null,
'status_history' => isset($meta['_status_history'][0]) ? maybe_unserialize($meta['_status_history'][0]) : array()
);
// 获取客户信息
$customer = get_userdata($post->post_author);
if ($customer) {
$data['customer'] = array(
'name' => $customer->display_name,
'email' => $customer->user_email
);
}
// 获取订单信息
if ($data['order_id']) {
$order = wc_get_order($data['order_id']);
if ($order) {
$data['order'] = array(
'total' => $order->get_total(),
'currency' => $order->get_currency(),
'date' => $order->get_date_created()->date('Y-m-d H:i:s')
);
}
}
return $data;
}
/**
* 处理退款
*/
public function process_refund($request) {
$request_id = $request['id'];
$parameters = $request->get_params();
$refund_processor = $GLOBALS['after_sales_system']->get_module('refund_processor');
if (!$refund_processor) {
return new WP_Error('module_not_found', '退款处理模块未找到', array('status' => 500));
}
$result = $refund_processor->process_refund($request_id);
if (is_wp_error($result)) {
return $result;
}
return array(
'success' => true,
'refund_id' => $result,
'message' => '退款处理成功'
);
}
/**
* 获取统计信息
*/
public function get_statistics($request) {
$params = $request->get_params();
$period = isset($params['period']) ? $params['period'] : 'month'; // day, week, month, year
$stats = array(
'total_requests' => $this->get_total_requests($period),
'by_status' => $this->get_requests_by_status($period),
'by_type' => $this->get_requests_by_type($period),
'refund_amount' => $this->get_refund_amount($period),
'processing_time' => $this->get_average_processing_time($period)
);
return $stats;
}
/**
* 获取请求总数
*/
private function get_total_requests($period) {
$date_query = $this->get_date_query_for_period($period);
$args = array(
'post_type' => 'after_sales_request',
'post_status' => 'publish',
'date_query' => $date_query,
'fields' => 'ids',
'posts_per_page' => -1
);
$query = new WP_Query($args);
return $query->found_posts;
}
}
/**
* 后台管理界面
*/
class After_Sales_Admin_Interface {
/**
* 初始化管理界面
*/
public function init() {
add_action('admin_menu', array($this, 'add_admin_pages'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
add_action('save_post_after_sales_request', array($this, 'save_request_meta'));
// 添加列表页列
add_filter('manage_after_sales_request_posts_columns', array($this, 'add_custom_columns'));
add_action('manage_after_sales_request_posts_custom_column', array($this, 'render_custom_columns'), 10, 2);
add_filter('manage_edit-after_sales_request_sortable_columns', array($this, 'make_columns_sortable'));
}
/**
* 添加管理页面
*/
public function add_admin_pages() {
// 主菜单
add_menu_page(
'售后管理',
'售后管理',
'manage_woocommerce',
'after-sales',
array($this, 'render_dashboard'),
'dashicons-undo',
56
);
// 子菜单
add_submenu_page(
'after-sales',
'售后仪表盘',
'仪表盘',
'manage_woocommerce',
'after-sales',
array($this, 'render_dashboard')
);
add_submenu_page(
'after-sales',
'售后请求',
'所有请求',
'manage_woocommerce',
'edit.php?post_type=after_sales_request'
);
add_submenu_page(
'after-sales',
'售后设置',
'设置',
'manage_woocommerce',
'after-sales-settings',
array($this, 'render_settings_page')
);
add_submenu_page(
'after-sales',
'售后报表',
'报表',
'manage_woocommerce',
'after-sales-reports',
array($this, 'render_reports_page')
);
}
/**
* 渲染仪表盘
*/
public function render_dashboard() {
?>
<div class="wrap after-sales-dashboard">
<h1 class="wp-heading-inline">售后管理仪表盘</h1>
<div class="dashboard-widgets">
<div class="widget-row">
<div class="widget-card">
<h3>待处理请求</h3>
<div class="widget-value" id="pending-requests">0</div>
<a href="<?php echo admin_url('edit.php?post_type=after_sales_request&status=pending'); ?>" class="widget-link">查看详情</a>
</div>
<div class="widget-card">
<h3>今日请求</h3>
<div class="widget-value" id="today-requests">0</div>
</div>
<div class="widget-card">
<h3>本月退款金额</h3>
<div class="widget-value" id="monthly-refund">¥0</div>
</div>
<div class="widget-card">
<h3>平均处理时间</h3>
<div class="widget-value" id="avg-processing-time">0小时</div>
</div>
</div>
<div class="widget-row">
<div class="widget-large-card">
<h3>请求趋势</h3>
<div class="chart-container">
<canvas id="requests-trend-chart"></canvas>
</div>
</div>
<div class="widget-large-card">
<h3>状态分布</h3>
/**
* 后台管理界面
*/
class After_Sales_Admin_Interface {
/**
* 初始化管理界面
*/
public function init() {
add_action('admin_menu', array($this, 'add_admin_pages'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
add_action('save_post_after_sales_request', array($this, 'save_request_meta'));
// 添加列表页列
add_filter('manage_after_sales_request_posts_columns', array($this, 'add_custom_columns'));
add_action('manage_after_sales_request_posts_custom_column', array($this, 'render_custom_columns'), 10, 2);
add_filter('manage_edit-after_sales_request_sortable_columns', array($this, 'make_columns_sortable'));
}
/**
* 添加管理页面
*/
public function add_admin_pages() {
// 主菜单
add_menu_page(
'售后管理',
'售后管理',
'manage_woocommerce',
'after-sales',
array($this, 'render_dashboard'),
'dashicons-undo',
56
);
// 子菜单
add_submenu_page(
'after-sales',
'售后仪表盘',
'仪表盘',
'manage_woocommerce',
'after-sales',
array($this, 'render_dashboard')
);
add_submenu_page(
'after-sales',
'售后请求',
'所有请求',
'manage_woocommerce',
'edit.php?post_type=after_sales_request'
);
add_submenu_page(
'after-sales',
'售后设置',
'设置',
'manage_woocommerce',
'after-sales-settings',
array($this, 'render_settings_page')
);
add_submenu_page(
'after-sales',
'售后报表',
'报表',
'manage_woocommerce',
'after-sales-reports',
array($this, 'render_reports_page')
);
}
/**
* 渲染仪表盘
*/
public function render_dashboard() {
?>
<div class="wrap after-sales-dashboard">
<h1 class="wp-heading-inline">售后管理仪表盘</h1>
<div class="dashboard-widgets">
<div class="widget-row">
<div class="widget-card">
<h3>待处理请求</h3>
<div class="widget-value" id="pending-requests">0</div>
<a href="<?php echo admin_url('edit.php?post_type=after_sales_request&status=pending'); ?>" class="widget-link">查看详情</a>
</div>
<div class="widget-card">
<h3>今日请求</h3>
<div class="widget-value" id="today-requests">0</div>
</div>
<div class="widget-card">
<h3>本月退款金额</h3>
<div class="widget-value" id="monthly-refund">¥0</div>
</div>
<div class="widget-card">
<h3>平均处理时间</h3>
<div class="widget-value" id="avg-processing-time">0小时</div>
</div>
</div>
<div class="widget-row">
<div class="widget-large-card">
<h3>请求趋势</h3>
<div class="chart-container">
<canvas id="requests-trend-chart"></canvas>
</div>
</div>
<div class="widget-large-card">
<h3>状态分布</h3>


